本文記錄在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登陸後看到首頁信息