­

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>          密&nbsp;&nbsp;&nbsp;碼:<input name="pwd"><br/>          <input type="checkbox" name="remeber"> 記住我<br/>          <input type="submit" value="Login">      </form>  </div>