EE颠覆者第九章企业开发security

Spring Security 基于Spring项目的安全框架,充分利用依赖注入 和 Aop 来实现安全的功能。

安全框架有两个重要的概念,

认证 Authentication :确认用户可以访问当前系统

和 授权 Authorization:确定用户 在当前系统下所拥有的功能权限。

security的配置

  1. 提供了过滤器来实现功能,

Dele gating Filter Proxy 过滤器到 Web Application Initializer

实际使用 让自己的 Initializer 类继承 Abstract Security Web Application Initializer 抽象类即可。(实现了 web…lizer接口),并通过 on Start up方法调用。

insert Spring Security Filter Chain (servletContext);

它为我们注册了, DelegatingFilterProxy.

代码:

    public class AppInitializer extends AbstractSecurityWebApplicationInitializer{
    }
  1. 配置

    和 MVC配置类似

    @Configuration
    @EnableWebSecurity //默认开启
    public class WebSecurityConfig extends WebSecurityConfigurerAdapter{//1 继承配置
    		//重写configure方法
    }
    
@Configuration  //MVC配置如下
public class WebMvcConfig extends WebMvcConfigurerAdapter{

用户认证

认证需要一套数据的来源,

授权对于某个用户有相应的角色权限。

	@Override
	protected void configure(AuthenticationManagerBuilder auth) 

内存认证

	@Override
	protected void configure(AuthenticationManagerBuilder auth) throws Exception {

		auth.inMemoryAuthentication()
				.withUser("lisi").password("123").roles("ROLE_ADMIN")
				.and()
				.withUser("wangwu").password("123").roles("ROLE_USER");

		
	}

jdbc认证

	@Resource
	DataSource dataSource;
	
	auth.jdbcAuthentication().dataSource(dataSource);
		
		对应JdbcDaoImpl
    "select username,password,enabled from users where username = ?";
    "select username,authority from authorities where username = ?";
    
    
    当然我们也可以自定义
    		auth.jdbcAuthentication().dataSource(dataSource)
				.usersByUsernameQuery(
				"select username,password,true from myusers where username = ?")
				
				.authoritiesByUsernameQuery(
				"select username,role from roles where username = ?");

普通的用户

出了内存和Jdbc,我们要自定义。 实现UserDetailService

	@Bean
	UserDetailsService customUserService(){ //2
		return new CustomUserService(); 
	}
		
		auth.userDetailsService(customUserService()); //3
		
	//详见实例	
	CustomUserService implements UserDetailsService	
		

请求授权

	@Override
	protected void configure(HttpSecurity http) throws Exception {

	}

匹配器包含:

antMatchers 使用ant 风格匹配路径

regexMatchers 使用正则表达式匹配路径

anyRequest 匹配所有路径

匹配了请求路径后,针对当前用户的信息对请求路径进行安全处理,提供弄了如下的安全方法

方法 用途
access(String) Spring EL表达式结果为true时可访问
anonymous() 匿名可访问
denyAll() 拒绝所有
fully Authenticated() 用户完全认证可访问(不是记住我 下的自动登录)
has Any Authority(String …) 如果用户有参数,则其中任一权限 可访问
has Any Role(String …) 如果用户有参数,则其中任一角色 可访问
has Authority(String …) 如果用户有参数,则其权限可访问
has IpAddress(String …) 如果用户来自参数中的IP可访问
has Role(String) 有参数中的角色可访问
permitAll() 用户可任一访问
rememberMe() 允许 记住我 登录的用户访问
authenticated() 用户登录后可访问
		http.authorizeRequests() //开始请求权限配置
				.antMatchers("/admin/**").hasRole("ROLE_ADMIN")
				.antMatchers("/user/**").hasAnyRole("ROLE_ADMIN","ROLE_USER")
				.anyRequest().authenticated();
				//admin下请求要有 admin的权限
				//user下请求,要有user 或者 admin的权限
				//其余的请求,都要登录
			http.
							                       formLogin().loginPage("/login").defaultSuccessUrl("/index").failureUrl("/login?error").permitAll()
				.and()
				.rememberMe().tokenValiditySeconds(1209600).key("myKey")
				.and()
				.logout().logoutUrl("/custom-logout").logoutSuccessUrl("/logout-success").permitAll();

				//formLogin定制登录
				//rememberMe开户cookie存储用户信息,1209600秒,就是2周,key就是 cookie中的私钥
				//logoutUrl注销URL路径

spring boot支持

在 autoconfigure.security包中

@Configuration
@ConditionalOnClass({ AuthenticationManager.class,
		GlobalAuthenticationConfigurerAdapter.class })
@EnableConfigurationProperties
@Import({ SpringBootWebSecurityConfiguration.class,
		AuthenticationManagerConfiguration.class,
		BootGlobalAuthenticationConfiguration.class })
public class SecurityAutoConfiguration {
}

//导入了SpringBootWebSecurityConfiguration

public class SpringBootWebSecurityConfiguration {

	private static List<String> DEFAULT_IGNORED = Arrays.asList("/css/**", "/js/**",
			"/images/**", "/**/favicon.ico");
    //1,自动配置了一个用户,账号为user,密码在程序启动时候出现
    //忽略上面的文件夹
    //自动配置的securityFilterChainRegistration的bean
}

@ConfigurationProperties(prefix = "security")//security开头
public class SecurityProperties implements SecurityPrerequisite {
}

配置

security:
  user:
    name: user
    password: 123
    role: USER
  require-ssl: false #是否需要SSL的支持
  enable-csrf: false #是否开启 跨域请求伪造 支持,默认关闭
  basic:
    enabled: true
    realm: Spring
    path: /**
    authorize-mode: authenticated #必须是一个认证的用户,默认不知道啥
  filter-order: 0
  headers:
    xss: false
    cache: false
    frame: false
    content-type: false
    hsts: all
  sessions: stateless
  ignored: #用逗号隔开 无需拦截的路径
  
  @Configuration //自己扩展用
public class WebSecurityConfig extends WebSecurityConfigurerAdapter{//1
}

实战

Pom

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jpa</artifactId>
		</dependency>
		<dependency>
			<groupId>com.oracle</groupId>
			<artifactId>ojdbc6</artifactId>
			<version>11.2.0.2.0</version>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-security</artifactId>
		</dependency>
	
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-thymeleaf</artifactId>
		</dependency>
		<dependency>
			<groupId>org.thymeleaf.extras</groupId>
			<artifactId>thymeleaf-extras-springsecurity4</artifactId>
		</dependency>
		

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>

配置文件

spring.datasource.driverClassName=oracle.jdbc.OracleDriver
spring.datasource.url=jdbc\:oracle\:thin\:@localhost\:1521\:xe
spring.datasource.username=boot
spring.datasource.password=boot

logging.level.org.springframework.security= INFO

spring.thymeleaf.cache=false

spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true

静态文件 和主类如下

src/main/resources/static/css/bootstrap.min.css


@SpringBootApplication
public class Ch91Application {
    public static void main(String[] args) {
        SpringApplication.run(Ch91Application.class, args);
    }
}

用户和角色

@Entity
public class SysUser implements UserDetails{ //1 实现这个接口,即为Security所使用的用户
	
	private static final long serialVersionUID = 1L;
	@Id
	@GeneratedValue
	private Long id;
	private String username;
	private String password;
    //配置用户和角色的对应关系
	@ManyToMany(cascade = {CascadeType.REFRESH},fetch = FetchType.EAGER)
	private List<SysRole> roles;
	
	
	@Override
	public Collection<? extends GrantedAuthority> getAuthorities() { //2 将用户的角色作为权限
		List<GrantedAuthority> auths = new ArrayList<GrantedAuthority>();
		List<SysRole> roles=this.getRoles();
		for(SysRole role:roles){
			auths.add(new SimpleGrantedAuthority(role.getName()));
		}
		return auths;
	}
	@Override
	public boolean isAccountNonExpired() {
		return true;
	}
	@Override
	public boolean isAccountNonLocked() {
		return true;
	}
	@Override
	public boolean isCredentialsNonExpired() {
		return true;
	}
	@Override
	public boolean isEnabled() {
		return true;
	}
	
	}
@Entity
public class SysRole {
	@Id
	@GeneratedValue
	private Long id;
	private String name;
	}

初始化 sql

spring.jpa.hibernate.ddl-auto=update
系统会自动生成 SYS_USER , SYS_ROLE 和 SYS_USER_ROLES

src/main/resources/data.sql

insert into SYS_USER (id,username, password) values (1,'wyf', 'wyf'); //管理员
insert into SYS_USER (id,username, password) values (2,'wisely', 'wisely'); //用户

insert into SYS_ROLE(id,name) values(1,'ROLE_ADMIN');
insert into SYS_ROLE(id,name) values(2,'ROLE_USER');

insert into SYS_USER_ROLES(SYS_USER_ID,ROLES_ID) values(1,1);
insert into SYS_USER_ROLES(SYS_USER_ID,ROLES_ID) values(2,2);

传值对象

public class Msg {
	private String title;
	private String content;
	private String etraInfo;
	
	public Msg(String title, String content, String etraInfo) {
		super();
		this.title = title;
		this.content = content;
		this.etraInfo = etraInfo;
	}
}

数据访问

public interface SysUserRepository extends JpaRepository<SysUser, Long>{
	
	SysUser findByUsername(String username);

}

自定义 UserDetailsService

public class CustomUserService implements UserDetailsService { //1
	@Autowired
	SysUserRepository userRepository;

	@Override
	public UserDetails loadUserByUsername(String username) { //2
		
		SysUser user = userRepository.findByUsername(username); 
		if(user == null){
			throw new UsernameNotFoundException("用户名不存在");
		}
		
		return user; //3
	}

}

与这些对应
	@Bean
	UserDetailsService customUserService(){ //2
		return new CustomUserService(); 
	}
		@Override //配置认证的
	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
		auth.userDetailsService(customUserService()); //3
		}

MVC配置

@Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter{
	
	@Override
	public void addViewControllers(ViewControllerRegistry registry) {
		registry.addViewController("/login").setViewName("login");
	} // /login跳转到login页面

}

spring Security配置

@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter{//1 集成
	
	@Bean
	UserDetailsService customUserService(){ //2
		return new CustomUserService(); 
	}

	@Override
	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
		auth.userDetailsService(customUserService()); //3
	}
	
	@Override
	protected void configure(HttpSecurity http) throws Exception {
		http.authorizeRequests()
						.anyRequest().authenticated() //4 所有的请求登录后才能访问
						.and()
						.formLogin()
							.loginPage("/login")
							.failureUrl("/login?error")
							.permitAll() //5定制登录,登录页面可 任意访问
						.and()
						.logout().permitAll(); //6 注销页面 可 任意访问
	}


}


@Order(100)
public abstract class WebSecurityConfigurerAdapter{} //集成抽象类

页面

src/main/resources/templates/login.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta content="text/html;charset=UTF-8"/>
<title>登录页面</title>
<link rel="stylesheet" th:href="@{css/bootstrap.min.css}"/>
<style type="text/css">
	body {
  padding-top: 50px;
}
.starter-template {
  padding: 40px 15px;
  text-align: center;
}
</style>
</head>
<body>
	
	 <nav class="navbar navbar-inverse navbar-fixed-top">
      <div class="container">
        <div class="navbar-header">
          <a class="navbar-brand" href="#">Spring Security演示</a>
        </div>
        <div id="navbar" class="collapse navbar-collapse">
          <ul class="nav navbar-nav">
           <li><a th:href="@{/}"> 首页 </a></li>
           
          </ul>
        </div><!--/.nav-collapse -->
      </div>
    </nav>
     <div class="container">

      <div class="starter-template">
       <p th:if="${param.logout}" class="bg-warning">已成功注销</p><!-- 1 -->
			<p th:if="${param.error}" class="bg-danger">有错误,请重试</p> <!-- 2 -->
			<h2>使用账号密码登录</h2>
			<form name="form" th:action="@{/login}" action="/login" method="POST"> <!-- 3 -->
				<div class="form-group">
					<label for="username">账号</label>
					<input type="text" class="form-control" name="username" value="" placeholder="账号" />
				</div>
				<div class="form-group">
					<label for="password">密码</label>
					<input type="password" class="form-control" name="password" placeholder="密码" />
				</div>
				<input type="submit" id="login" value="Login" class="btn btn-primary" />
			</form>
      </div>

    </div>
		
</body>
</html>
src/main/resources/templates/home.html


<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" 
	  xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4"><!-- 1 -->
<head>
<meta content="text/html;charset=UTF-8"/>
<title sec:authentication="name"></title> <!-- 2 --> 获得到当前用户的用户名
<link rel="stylesheet" th:href="@{css/bootstrap.min.css}" />
<style type="text/css">
body {
  padding-top: 50px;
}
.starter-template {
  padding: 40px 15px;
  text-align: center;
}
</style>
</head>
<body>
	 <nav class="navbar navbar-inverse navbar-fixed-top">
      <div class="container">
        <div class="navbar-header">
          <a class="navbar-brand" href="#">Spring Security演示</a>
        </div>
        <div id="navbar" class="collapse navbar-collapse">
          <ul class="nav navbar-nav">
           <li><a th:href="@{/}"> 首页 </a></li>
           
          </ul>
        </div><!--/.nav-collapse -->
      </div>
    </nav>
    
    
     <div class="container">

      <div class="starter-template">
      	<h1 th:text="${msg.title}"></h1>
		
		<p class="bg-primary" th:text="${msg.content}"></p>
		
		<div sec:authorize="hasRole('ROLE_ADMIN')"> <!-- 3 --> 只有当前用户是管理员 才显示
		 	<p class="bg-info" th:text="${msg.etraInfo}"></p>
		</div>	
		
		<div sec:authorize="hasRole('ROLE_USER')"> <!-- 4--> 只有当前用户是用户才展示
		 	<p class="bg-info">无更多信息显示</p>
		</div>	
		
        <form th:action="@{/logout}" method="post">
            <input type="submit" class="btn btn-primary" value="注销"/><!-- 5 -->
        </form>
      </div>

    </div>
    
	
</body>
</html>

控制器

@Controller
public class HomeController {
	
	@RequestMapping("/")
	public String index(Model model){
		Msg msg =  new Msg("测试标题","测试内容","额外信息,只对管理员显示");
		model.addAttribute("msg", msg);
		return "home";
	}

}
http://localhost:8080/  wyf
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章