本文记录在SpringBoot使用SpringSecurity进行安全访问控制。html
Spring Security是一个可以为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架。它提供了一组能够在Spring应用上下文中配置的Bean,充分利用了Spring IoC,DI(控制反转Inversion of Control ,DI:Dependency Injection 依赖注入)和AOP(面向切面编程)功能,为应用系统提供声明式的安全访问控制功能,减小了为企业系统安全控制编写大量重复代码的工做。web
目前在咱们项目中使用的是RBAC基于角色的权限访问控制(Role-Based Access Control),用户与角色关联,角色与权限相关联,用户经过角色间接的获得权限。关系以下图spring
用户:权限的拥有者数据库
角色:一些权限的集合编程
权限:操做的对象或资源安全
用户拥有某种角色,从而拥有了对资源的访问操做权限,在访问时SpringSecurity会对全部请求进行拦截,有权限的请求放行,不然拦截。app
SpringBoot对SpringSecurity作了支持,要使用的话很方便,只须要引入相应的依赖(spring-boot-starter-security)就能够了。框架
示例代码主要完成如下功能:ide
1 系统的首页和登陆页面及一些静态资源(CSS,JS),默认全部用户均可以访问;spring-boot
2 除了第一步的,其余的全部资源路径访问均须要用户经过认证;
3 登陆用户在页面只能看到拥有的角色所对应的权限(资源或操做);
修改pom.xml文件,添加依赖
<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-web</artifactId>
</dependency>
建立配置类,继承 WebSecurityConfigurerAdapter,重写一些配置方法
@Configuration
@EnableWebSecurity
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/", "/static/**").permitAll()
.anyRequest().authenticated()
.and().formLogin().loginPage("/login").permitAll()
.successForwardUrl("/main")
.failureUrl("/login")
.and().logout()
.logoutUrl("/logout").permitAll()
.logoutSuccessUrl("/login");
}
}
@EnableWebSecurity 用来讲明开启安全认证
configure(HttpSecurity http) 配置相关访问操做的策略
.antMatchers("/", "/static/**").permitAll() 说明项目根路径/ 及static路径下的静态资源能够被匿名访问
.anyRequest().authenticated() 说明除了能够被匿名访问的资源外,其余全部资源的访问都要通过认证
.formLogin() 说明使用用户自定义的登陆,若是不配置的话,会使用SpringSecurity默认提供的登陆页面,/login 资源能够被匿名访问,登陆成功后访问/main,失败后访问/login
.logout() 退出功能,SpringSecurity默认对/logout作了监控
用户登陆就是对当前用户的身份信息作认证,咱们须要对相应的方法作重写
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService()).passwordEncoder(passwordEncoder());
}
指定使用自定义的实现用户认证及受权的userDetailsService和密码的加密器
密码加密器
public BCryptPasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
认证与受权
@Bean
@Override
protected UserDetailsService userDetailsService() {
return new UserDetailsService(){
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//此处模拟数据库查询用户
User user = new User();
user.setUserId(2);
user.setUsername(username);
user.setPassword("$2a$10$GS71hBKk0MaykCWZC/eo2e7Y0Z77zDNCYE06xxAmW37gl850E6I4G");
user.setTelephone("13000000000");
user.setEmail("13000000000@qq.com");
if(user == null) throw new UsernameNotFoundException("User name:"+username+" not fount");
SecurityUser securityUser= new SecurityUser(user);
return securityUser;
}
};
}
/**
* 真正用于登陆验证的安全用户(UserDetails)
*/
class SecurityUser extends User implements UserDetails {
/**
* 用户权限
*/
private Set<SimpleGrantedAuthority> permissions;
public Set<SimpleGrantedAuthority> getPermissions() {
return permissions;
}
public void setPermissions(Set<SimpleGrantedAuthority> permissions) {
this.permissions = permissions;
}
public SecurityUser(User user){
if(user != null){
this.setUserId(user.getUserId());
this.setUsername(user.getUsername());
this.setPassword(user.getPassword());
this.setEmail(user.getEmail());
this.setTelephone(user.getTelephone());
Set<SimpleGrantedAuthority> gasSet = (Set<SimpleGrantedAuthority>) getAuthorities();
if(gasSet.size()>0){
this.setPermissions(gasSet);
}
}
}
/**
* 获取用户权限
* @return
*/
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
//要返回的用户权限集合
Set<GrantedAuthority> permsSet = new HashSet<GrantedAuthority>();
//模拟数据库查询用户所拥有的角色对应的权限
permsSet.add(new SimpleGrantedAuthority("/user/add"));
permsSet.add(new SimpleGrantedAuthority("/user/edit"));
permsSet.add(new SimpleGrantedAuthority("/user/delete"));
permsSet.add(new SimpleGrantedAuthority("/user/list"));
//区分不一样用户拥有不一样权限,admin用户加权限
if (this.getUsername().equals("admin")) {
permsSet.add(new SimpleGrantedAuthority("/role/list"));
permsSet.add(new SimpleGrantedAuthority("/role/add"));
permsSet.add(new SimpleGrantedAuthority("/role/edit"));
permsSet.add(new SimpleGrantedAuthority("/role/delete"));
}
return permsSet;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
控制器
@Controller
public class LoginController {
/**
* 访问根路径时跳转到index页面
* @return
*/
@GetMapping("/")
public String root(){
return "index";
}
/**
* 跳转到登陆页面
* @return
*/
@GetMapping("/login")
public String login(){
return "login";
}
/**
* 登陆成功后访问
* @return
*/
@PostMapping("/main")
public String main(){
return "main";
}
}
index页面
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>index</title>
</head>
<body>
this is index page<br/>
<a th:href="@{/user/login}">登陆</a>
</body>
</html>
登陆页面
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>login</title>
</head>
<body>
this is login page
<form th:action="@{/login}" method="post">
<input type="text" th:id="username" th:name="username" value="" >
<input type="password" th:id="password" th:name="password" value="">
<input type="submit" th:value="提交" >
</form>
</body>
</html>
main页面
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1 th:text="这是主页面">text</h1>
<form th:action="@{/logout}" method="post"><button th:type="submit" th:text="退出">text</button></form>
<hr/>
<th:block sec:authorize="hasAuthority('/user/add')">
<a th:href="@{/user/add}">添加用户</a>
</th:block>
<th:block sec:authorize="hasAuthority('/user/edit')">
<a th:href="@{/user/edit}">修改用户</a>
</th:block>
<th:block sec:authorize="hasAuthority('/user/delete')">
<a th:href="@{/user/delete}">删除用户</a>
</th:block>
<th:block sec:authorize="hasAuthority('/user/list')">
<a th:href="@{/user/list}">查询用户</a>
</th:block>
<th:block sec:authorize="hasAuthority('/role/add')">
<a th:href="@{/role/add}">添加角色</a>
</th:block>
<th:block sec:authorize="hasAuthority('/role/delete')">
<a th:href="@{/role/delete}">删除角色</a>
</th:block>
<th:block sec:authorize="hasAuthority('/role/edit')">
<a th:href="@{/role/edit}">修改角色</a>
</th:block>
<th:block sec:authorize="hasAuthority('/role/list')">
<a th:href="@{/role/list}">查询角色</a>
</th:block>
</body>
</html>
sec:authorize="hasAuthority('')" 说明当用户拥有此权限的时候,操做对用户可见,不然不可见
分别用user用户和admin登陆后看到首页信息