SpringSecurity學(xué)習(xí)記錄

1、簡介
Spring Security是一個功能強大且高度可定制的身份驗證和訪問控制框架。
2、與Shiro區(qū)別:
Shiro是一個強大而靈活的開源安全框架,能夠非常清晰的處理認(rèn)證、授權(quán)、管理會話以及密碼加
密。如下是它所具有的特點:
①易于理解的 Java Security API;
②簡單的身份認(rèn)證(登錄),支持多種數(shù)據(jù)源(LDAP,JDBC,Kerberos,ActiveDirectory
等);
③對角色的簡單的鑒權(quán)(訪問控制),支持細(xì)粒度的鑒權(quán);
④支持一級緩存,以提升應(yīng)用程序的性能;
⑤內(nèi)置的基于 POJO 企業(yè)會話管理,適用于 Web 以及非 Web 的環(huán)境;
⑥異構(gòu)客戶端會話訪問;
⑦非常簡單的加密 API;
⑧不跟任何的框架或者容器捆綁,可以獨立運行。
Spring Security:
除了不能脫離Spring,shiro功能Security都具備;并且Spring Security對Oauth openid也有支持,shiro需要手動實現(xiàn),Spring Security權(quán)限顆粒度也更高

3、應(yīng)用場景
①用戶登錄,基于web開發(fā)的項目登錄功能
②用戶授權(quán)
③單一登錄:一個賬號同一時間只能在一個地方進(jìn)行登錄,如果在其他登錄二次登錄,則剔除前面的登錄操作
④集成CAS,做單點登錄
⑤集成Oauth2,做授權(quán)登錄(第三方登錄等),也可以實現(xiàn)cas

4.Spring Security認(rèn)證基本原理
①使用方式:引入依賴

  <!--添加Spring Security 依賴 --> 
  <dependency> 
      <groupId>org.springframework.boot</groupId> 
      <artifactId>spring-boot-starter-security</artifactId> 
  </dependency>

②原理:
Spring Security功能的實現(xiàn)主要是由一系列過濾器相互配合完
成。也稱之為過濾器鏈
https://www.processon.com/diagraming/60889b756376896ee106ae10

Spring Security執(zhí)行流程圖.png

③認(rèn)證方式:
⑴HttpBasic認(rèn)證:Spring Security實現(xiàn)登錄驗證最簡單的一種方式,Security 4.X版本,無需任何配置,啟動項目訪問則會彈出默認(rèn)的httpbasic認(rèn)證,在spring security 5.x默認(rèn)的驗證模式已經(jīng)是表單模式。HttpBasic模式要求傳輸?shù)挠脩裘艽a使用Base64模式進(jìn)行加密。
⑵formLogin登錄認(rèn)證模式:spring boot2.0以上版本(依賴Security 5.X版本)默認(rèn)會生成一個登錄頁面.同樣,我們也可以選擇自定義登錄頁面

④安全構(gòu)建器 HttpSecurity 和 WebSecurity 的區(qū)別:
⑴、WebSecurity 不僅通過 HttpSecurity 定義某些請求的安全控制,也通過其他方式定義其他某些請求可以忽略安全控制;
⑵、HttpSecurity 僅用于定義需要安全控制的請求(當(dāng)然 HttpSecurity 也可以指定某些請求不需要安全控制);
⑶、可以認(rèn)為 HttpSecurity 是 WebSecurity 的一部分, WebSecurity 是包含 HttpSecurity 的更大的一個概念;
⑷、構(gòu)建目標(biāo)不同:WebSecurity 構(gòu)建目標(biāo)是整個 Spring Security 安全過濾器 FilterChainProxy`;HttpSecurity 的構(gòu)建目標(biāo)僅僅是 FilterChainProxy 中的一個 SecurityFilterChain 。

⑤表單登錄:
⑴UsernamePasswordAuthenticationFilter過濾器源碼分析

public static final String SPRING_SECURITY_FORM_USERNAME_KEY = "username";
public static final String SPRING_SECURITY_FORM_PASSWORD_KEY = "password";
private static final AntPathRequestMatcher DEFAULT_ANT_PATH_REQUEST_MATCHER = new AntPathRequestMatcher("/login", "POST");

表單中的input的name值是username和password, 并且表單提交的路徑為 /login , 表單提交方式method為 post , 這些可以修改為自定義的值.即在自定義SecurityConfig配置類中:

  http.formLogin()//開啟表單認(rèn)證
            .loginPage("/toLoginPage")//自定義登錄頁面
            .loginProcessingUrl("/login")//自定義表單提交路徑
            .usernameParameter("username")
            .passwordParameter("password")//自定義input值
            .successForwardUrl("/")//指定登錄成功后跳轉(zhuǎn)的路徑
            .and().authorizeRequests().antMatchers("/toLoginPage").permitAll()//放行登陸頁面
        .anyRequest().authenticated();
    /**
     * 關(guān)閉csrf防護
     */
    http.csrf().disable();
    /**
     * 加載同源域名下iframe頁面
     */
    http.headers().frameOptions().sameOrigin();

⑵基于數(shù)據(jù)庫實現(xiàn)認(rèn)證功能:
1、實現(xiàn)security的一個UserDetailsService接口, 重寫這個接口里面
loadUserByUsername方法:

  /**
 * 根據(jù)username查詢用戶實體
 * @param username
 * @return
 * @throws UsernameNotFoundException
 */
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    User user = userService.findByUsername(username);
    if(user==null){
        throw new UsernameNotFoundException(username+"用戶沒有找到");
    }
    //聲明權(quán)限集合,因為構(gòu)造方法中不能傳null值
    Collection<? extends GrantedAuthority> authorities = new ArrayList<>();

    UserDetails userDetails=new org.springframework.security.core.userdetails.User(user.getUsername()
    ,"{bcrypt}"+user.getPassword() //{noop}表示不加密 bcrypt表示bcrypt算法加密
    ,true //用戶是否啟用 true 代表啟用
    ,true // 用戶是否過期 true 代表未過期
    ,true // 用戶憑據(jù)是否過期 true 代表未過期
    ,true // 用戶是否鎖定 true 代表未鎖定
    ,authorities);
    return userDetails;
}

2、在SecurityConfig配置類中指定自定義用戶認(rèn)證

  /**
 * 身份驗證管理器
 * @param auth
 * @throws Exception
 */
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    //使用自定義用戶認(rèn)證
    auth.userDetailsService(myUserDetailsService);
}

⑶密碼加密認(rèn)證:
在基于數(shù)據(jù)庫完成用戶登錄的過程中,我們所是使用的密碼是明文的,規(guī)則是通過對密碼明文添加{noop} 前綴。
Spring Security 中 PasswordEncoder 就是我們對密碼進(jìn)行編碼的工具接口。該接口只有兩個功能:一個是匹配驗證。另一個是密碼編碼

  public interface PasswordEncoder {
    String encode(CharSequence var1);

    boolean matches(CharSequence var1, String var2);

    default boolean upgradeEncoding(String encodedPassword) {
    return false;
    }
  }

密碼工廠PasswordEncoderFactories:

  public class PasswordEncoderFactories {
      public static PasswordEncoder createDelegatingPasswordEncoder() {
          String encodingId = "bcrypt";
          Map<String, PasswordEncoder> encoders = new HashMap();
          encoders.put(encodingId, new BCryptPasswordEncoder());
          encoders.put("ldap", new LdapShaPasswordEncoder());
          encoders.put("MD4", new Md4PasswordEncoder());
          encoders.put("MD5", new MessageDigestPasswordEncoder("MD5"));
          encoders.put("noop", NoOpPasswordEncoder.getInstance());
          encoders.put("pbkdf2", new Pbkdf2PasswordEncoder());
          encoders.put("scrypt", new SCryptPasswordEncoder());
          encoders.put("SHA-1", new MessageDigestPasswordEncoder("SHA-1"));
          encoders.put("SHA-256", new MessageDigestPasswordEncoder("SHA-256"));
          encoders.put("sha256", new StandardPasswordEncoder());
          encoders.put("argon2", new Argon2PasswordEncoder());
      return new DelegatingPasswordEncoder(encodingId, encoders);
      }

      private PasswordEncoderFactories() {
      }
  }

⑦退出登錄:LogoutSuccessHandler處理器

  //設(shè)置退出url 
  //自定義退出處理
  and().logout().logoutUrl("/logout").logoutSuccessHandler(myAuthenticationService)

⑧圖形碼驗證:
spring security添加驗證碼大致可以分為三個步驟:
⑴、根據(jù)隨機數(shù)生成驗證碼圖片;
⑵、將驗證碼圖片顯示到登錄頁面;
⑶、認(rèn)證流程中加入驗證碼校驗。
Spring Security的認(rèn)證校驗是由UsernamePasswordAuthenticationFilter過濾器完成的,所以驗證碼校驗邏輯應(yīng)該在這個過濾器之前:
自定義驗證碼過濾器ValidateCodeFilter,需要繼承OncePerRequestFilter確保在一次請求只通過一次filter,而不 需要重復(fù)執(zhí)行
⑨session管理:
⑴、會話超時:
設(shè)置session管理和失效后跳轉(zhuǎn)地址

  http.sessionManagement() //設(shè)置session管理 
  .invalidSessionUrl("/toLoginPage")// session無效后跳轉(zhuǎn)的路徑, 默認(rèn)是登錄頁面

⑵、并發(fā)控制:SessionManagementFilter
修改超時時間:

  #session設(shè)置 #配置session超時時間 
  server.servlet.session.timeout=600

設(shè)置最大會話數(shù)量:

  http.sessionManagement()  //設(shè)置session管理        
  .invalidSessionUrl("/toLoginPage") // session無效后跳轉(zhuǎn)的路徑, 默 認(rèn)是登錄頁面 
  .maximumSessions(1)//設(shè)置session最大會話數(shù)量 ,1同一時間只能有一個 用戶登錄 
  .expiredUrl("/toLoginPage");//設(shè)置session過期后跳轉(zhuǎn)路徑

阻止用戶第二次登錄:sessionManagement也可以配置maxSessionsPreventsLogin:boolean值,當(dāng)達(dá)到maximumSessions設(shè)置的最大會話個數(shù)時阻止登錄。

  http.sessionManagement()//設(shè)置session管理 
  .invalidSessionUrl("/toLoginPage")// session無效后跳轉(zhuǎn)的路徑, 默 認(rèn)是登錄頁面 
  .maximumSessions(1)//設(shè)置session最大會話數(shù)量 ,1同一時間只能有一個 用戶登錄 
  .maxSessionsPreventsLogin(true)//當(dāng)達(dá)到最大會話個數(shù)時阻止登錄。       
  .expiredUrl("/toLoginPage");//設(shè)置session過期后跳轉(zhuǎn)路徑

5、SpringSecurity授權(quán)
①內(nèi)置表達(dá)式:
Spring Security 使用Spring EL來支持,主要用于Web訪問和方法安全上, 可以通過表達(dá)式來判斷是否具有訪問權(quán)限. 下面是Spring Security常用的內(nèi)置表達(dá)式.ExpressionUrlAuthorizationConfigurer定義了所有的表達(dá)式

  表達(dá)式           說明
  permitAll        指定任何人都允許訪問。
  denyAll 指定任何人都不允許訪問
  anonymous 指定匿名用戶允許訪問。
  rememberMe 指定已記住的用戶允許訪問。
  authenticated 指定任何經(jīng)過身份驗證的用戶都允許訪問,不包含anonymous
  fullyAuthenticated 指定由經(jīng)過身份驗證的用戶允許訪問,不包含anonymous和rememberMe
  hasRole(role) 指定需要特定的角色的用戶允許訪問, 會自動在角色前面插入'ROLE_'
  hasAnyRole([role1,role2]) 指定需要任意一個角色的用戶允許訪問, 會自動在角色前面插入'ROLE_'
  hasAuthority(authority) 指定需要特定的權(quán)限的用戶允許訪問
  hasAnyAuthority([authority,authority]) 指定需要任意一個權(quán)限的用戶允許訪問
  hasIpAddress(ip) 指定需要特定的IP地址可以訪問

②url安全表達(dá)式:
⑴、設(shè)置url訪問權(quán)限:

  // 設(shè)置/user/** 訪問需要ADMIN角色 
http.authorizeRequests().antMatchers("/user/**").hasRole("ADMIN"); 
  // 設(shè)置/user/** 訪問需要PRODUCT角色和IP地址為127.0.0.1 
  .hasAnyRole("PRODUCT,ADMIN")     
  .http.authorizeRequests().antMatchers("/product/**") .access("hasAnyRole('ADMIN,PRODUCT') and hasIpAddress('127.0.0.1')"); 
  // 設(shè)置自定義權(quán)限不足信息
  .http.exceptionHandling().accessDeniedHandler(accessDeniedHandler);

⑵、 MyAccessDeniedHandler自定義權(quán)限不足類,實現(xiàn)AccessDeniedHandler處理器
⑶、設(shè)置用戶對應(yīng)的角色權(quán)限

③在Web 安全表達(dá)式中引用自定義Bean授權(quán):
⑴、定義自定義授權(quán)類,類似:

  /*** 自定義授權(quán)類 */ 
  @Component public class MyAuthorizationService { 
  /*** 檢查用戶是否有對應(yīng)的訪問權(quán)限 
  ** @param authentication 登錄用戶 
  * @param request 請求對象 
  * @return */ 
  public boolean check(Authentication authentication, HttpServletRequest request) {
    User user = (User) authentication.getPrincipal(); 
    // 獲取用戶所有權(quán)限 
    Collection<GrantedAuthority> authorities = user.getAuthorities(); 
    // 獲取用戶名 
    String username = user.getUsername(); 
    // 如果用戶名為admin,則不需要認(rèn)證 
    if (username.equalsIgnoreCase("admin")) { 
          return true; 
    } else { 
      // 循環(huán)用戶的權(quán)限, 判斷是否有ROLE_ADMIN權(quán)限, 有返回true 
      for (GrantedAuthority authority : authorities) { 
          String role = authority.getAuthority(); 
          if ("ROLE_ADMIN".equals(role)) { 
              return true; 
          } 
      }
     }
              return false;
     } 
    }

⑵、配置類

  //使用自定義Bean授權(quán) 
  http.authorizeRequests().antMatchers("/user/**")
  .access("@myAuthorizationService.check(authentication,request)");

⑶、攜帶路徑變量

  //使用自定義Bean授權(quán),并攜帶路徑參數(shù) 
  http.authorizeRequests().antMatchers("/user/delete/{id}"). 
  access("@myAuthorizationService.check(authentication,request,#id)");

④Method安全表達(dá)式:
針對方法級別的訪問控制比較復(fù)雜, spring security 提供了4種注解分別是:
@PreAuthorize :注解適合進(jìn)入方法前的權(quán)限驗證
@PostAuthorize :在方法執(zhí)行后再進(jìn)行權(quán)限驗證,適合驗證帶有返回值的權(quán)
限, Spring EL 提供返回對象能夠在表達(dá)式語言中獲取到返回對象的 returnObject
@PreFilter : 可以用來對集合類型的參數(shù)進(jìn)行過濾, 將不符合條件的元素剔除集合
@PostFilter : 可以用來對集合類型的返回值進(jìn)行過濾, 將不符合條件的元素剔除集合

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容