Security整合spring boot
- 2019 年 11 月 6 日
- 筆記
1、基礎概念
Spring Security是一個能夠為基於Spring的企業應用系統提供聲明式的安全訪問控制解決方案的安全框架。它提供了一組可以在Spring應用上下文中配置的Bean,充分利用了Spring的IOC,DI,AOP(面向切面編程)功能,為應用系統提供聲明式的安全訪問控制功能,減少了為安全控制編寫大量重複代碼的工作。
2、核心API解讀
1、SecurityContextHolder
最基本的對象,保存着當前會話用戶認證,權限,鑒權等核心數據。SecurityContextHolder默認使用ThreadLocal策略來存儲認證信息,與線程綁定的策略。用戶退出時,自動清除當前線程的認證信息。
初始化源碼:明顯使用ThreadLocal線程。
private static void initialize() { if (!StringUtils.hasText(strategyName)) { strategyName = "MODE_THREADLOCAL"; } if (strategyName.equals("MODE_THREADLOCAL")) { strategy = new ThreadLocalSecurityContextHolderStrategy(); } else if (strategyName.equals("MODE_INHERITABLETHREADLOCAL")) { strategy = new InheritableThreadLocalSecurityContextHolderStrategy(); } else if (strategyName.equals("MODE_GLOBAL")) { strategy = new GlobalSecurityContextHolderStrategy(); } else { try { Class<?> clazz = Class.forName(strategyName); Constructor<?> customStrategy = clazz.getConstructor(); strategy = (SecurityContextHolderStrategy)customStrategy.newInstance(); } catch (Exception var2) { ReflectionUtils.handleReflectionException(var2); } } ++initializeCount; }
2、Authentication
源碼
public interface Authentication extends Principal, Serializable { Collection<? extends GrantedAuthority> getAuthorities(); Object getCredentials(); Object getDetails(); Object getPrincipal(); boolean isAuthenticated(); void setAuthenticated(boolean var1) throws IllegalArgumentException; }
源碼分析
1)、getAuthorities,權限列表,通常是代表權限的字符串集合;
2)、getCredentials,密碼,認證之後會移出,來保證安全性;
3)、getDetails,請求的細節參數;
4)、getPrincipal, 核心身份信息,一般返回UserDetails的實現類。
3、UserDetails
封裝了用戶的詳細的信息。
public interface UserDetails extends Serializable { Collection<? extends GrantedAuthority> getAuthorities(); String getPassword(); String getUsername(); boolean isAccountNonExpired(); boolean isAccountNonLocked(); boolean isCredentialsNonExpired(); boolean isEnabled(); }
4、 UserDetailsService
實現該接口,自定義用戶認證流程,通常讀取數據庫,對比用戶的登錄信息,完成認證,授權。
public interface UserDetailsService { UserDetails loadUserByUsername(String var1) throws UsernameNotFoundException; }
5、 AuthenticationManager
認證流程頂級接口。可以通過實現AuthenticationManager接口來自定義自己的認證方式
Spring提供了一個默認的實現,ProviderManager。
public interface AuthenticationManager { Authentication authenticate(Authentication var1) throws AuthenticationException; }
3.整合
1、流程描述
1)、模擬增刪改查 2)、未登錄授權都不可以訪問 3)、登錄後根據用戶權限,訪問指定頁面 4)、對於未授權頁面,訪問返回403:資源不可用
2、核心依賴
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency>
3、核心配置
package com.Boot.config; import com.Boot.Security.UserDetailServiceImpl; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; /** * @Classname SpringSecurityConfig * @Description TODO * @Date 2019/11/6 19:59 * @Created by 遠 */ @Configuration @EnableWebSecurity //啟動springSecurity啟動鏈 public class SpringSecurityConfig extends WebSecurityConfigurerAdapter { /** * @return * @Author 遠 * @Description 代替認證管理器 * @Param **/ /** * 自定義認證數據源 */ @Override protected void configure(AuthenticationManagerBuilder builder) throws Exception{ builder.userDetailsService(userDetailService()) .passwordEncoder(passwordEncoder()); } @Bean public UserDetailServiceImpl userDetailService (){ return new UserDetailServiceImpl () ; } /** * 密碼加密 */ @Bean public BCryptPasswordEncoder passwordEncoder(){ return new BCryptPasswordEncoder(); } /** * @Author 遠 * @Description 硬編碼一個用戶用於測試 * @Param * @return **/ /*@Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.inMemoryAuthentication().withUser("ljw").password("123").authorities("ROLE_ADD"); }*/ /** * @return * @Author 遠 * @Description 代替之前配置<security:http></security:http> **/ @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() //antMatchers指定頁面,hasAnyAuthority指定擁有什麼樣的權限的用戶可以訪問 .antMatchers("/add").hasAnyAuthority("ROLE_ADD") .antMatchers("/update").hasAnyAuthority("ROLE_UPDATE") .antMatchers("/list").hasAnyAuthority("ROLE_SELECT") .antMatchers("/delete").hasAnyAuthority("ROLE_DELETE") //放行index和login頁面 .antMatchers("/login").permitAll() .antMatchers("/index").permitAll() //攔截全部 .antMatchers("/**") .fullyAuthenticated() .and() //解決頁面csrf報錯 .csrf().disable(); // 配置登錄功能 http.formLogin().usernameParameter("user") .passwordParameter("pwd") .loginPage("/userLogin"); // 註銷成功跳轉首頁 http.logout().logoutSuccessUrl("/"); //開啟記住我功能 http.rememberMe().rememberMeParameter("remeber"); } }
4、認證
package com.Boot.Security; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.stereotype.Service; import javax.annotation.Resource; import java.util.ArrayList; import java.util.List; /** * @Classname UserDetailServiceImpl * @Description 認證 * @Date 2019/11/6 22:22 * @Created by 遠 */ @Service public class UserDetailServiceImpl implements UserDetailsService { @Resource private UserRoleMapper userRoleMapper ; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { // 這裡可以捕獲異常,使用異常映射,拋出指定的提示信息 // 用戶校驗的操作 // 假設密碼是數據庫查詢的 123 String password = "$2a$10$XcigeMfToGQ2bqRToFtUi.sG1V.HhrJV6RBjji1yncXReSNNIPl1K"; // 假設角色是數據庫查詢的 //查詢數據里所有的用戶對應角色的權限 List<String> roleList = userRoleMapper.selectByUserName(username) ; List<GrantedAuthority> grantedAuthorityList = new ArrayList<>() ; /* * Spring Boot 2.0 版本踩坑 * 必須要 ROLE_ 前綴, 因為 hasRole("LEVEL1")判斷時會自動加上ROLE_前綴變成 ROLE_LEVEL1 , * 如果不加前綴一般就會出現403錯誤 * 在給用戶賦權限時,數據庫存儲必須是完整的權限標識ROLE_LEVEL1 */ if (roleList != null && roleList.size()>0){ for (String role : roleList){ //將所有的角色遍歷到GrantedAuthority grantedAuthorityList.add(new SimpleGrantedAuthority(role)) ; } } //返回用戶,用戶的用戶名,密碼,以及權限 return new User(username,password,grantedAuthorityList); } }
5、接口
package com.Boot.Controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; /** * @Classname productController * @Description 增刪改查 * @Date 2019/11/1 14:41 * @Created by 遠 */ @Controller public class productController { /** * @Author 遠 * @Description * @Param * @return **/ @RequestMapping("/add") public String add() { return "product/add"; } /** * @Author 遠 * @Description 商品修改 **/ @RequestMapping("update") public String Update() { return "product/update"; } /** * @Author 遠 * @Description 商品顯示 **/ @RequestMapping("list") public String list() { return "product/list"; } /** * @Author 遠 * @Description 商品刪除 **/ @RequestMapping("delete") public String del() { return "product/delete"; } @RequestMapping("/login") public String login(){ return "login"; } @RequestMapping("index") public String index(){ return"index"; } @RequestMapping("403") public String forbidden(){ return "403"; } }
6、前端登錄頁面
這裡要和Security的配置文件相對應。
<div align="center"> <form th:action="@{/userLogin}" method="post"> 用戶名:<input name="user"/><br> 密 碼:<input name="pwd"><br/> <input type="checkbox" name="remeber"> 記住我<br/> <input type="submit" value="Login"> </form> </div>