Spring Security Web 和 OAuth2

前言<a id="sec-1"></a>

Spring Security 是一個多模塊的項目,以前梳理了一下 Spring Security 認證流程,如今才發現,梳理的那部份內容更多的只是 Spring Security Core 這個核心模塊中的內容。html

平常使用時,還會更多的涉及 Spring Security Web 和 Spring Security OAuth2 中的東西,這篇博客的主要內容即是梳理一下這三者之間的關係,瞭解一下各自發揮的做用。java

Spring Security Core<a id="sec-2"></a>

Spring Security Core 在整個 Spring Security 框架中扮演着重要的角色,提供了有關於認證和權限控制相關的抽象。git

然而,在使用的過程當中,咱們接觸的更多的多是和認證相關的抽象,好比:github

  • 經過 AuthenticationManager 提供了進行用戶認證方法的抽象,容許經過 ProviderManagerAuthenticationProvider 來組裝和實現本身的認證方法
  • 經過 UserDetailsUserDetailsService 提供了用戶詳細信息和獲取用戶詳細信息方式的抽象
  • 經過 Authentication 提供了用戶認證信息和認證結果的抽象
  • 經過 SecurityContextSecurityContextHolder 提供了保存認證結果的方式
  • ……

這些東西其實就是將傳統的認證流程中的關鍵組成單獨抽象了出來,結合傳統的認證流程能夠很容易的理解這些組件之間的關係,也能夠看這張來自 Spring Security(一) —— Architecture Overview | 芋道源碼 —— 純源碼解析博客 的一張圖片:web

<img src="https://user-gold-cdn.xitu.io/2019/11/2/16e2c58cc7573921?w=995&h=562&f=png&s=45816">spring

而權限控制部分的抽象,主要就是 AccessDecisionManagerAccessDecisionVoter 了,這兩個東西我目前尚未手動操做過,只能說,Spring Security Web 提供的服務太貼心, 權限控制部分的實現並不須要我操太多心。安全

關於 Spring Security Core 模塊更多的內容能夠參考:服務器

Spring Security Web<a id="sec-3"></a>

若是說 Spring Security Core 只是提供了認證和權限控制相關的抽象的話,Spring Security Web 便爲咱們提供了這些抽象的具體實現與應用。session

Spring Security Web 經過 過濾器鏈 來實現了和 Web 安全相關的一系列功能,而用戶的認證和權限控制只是其中的一部分,在這部分的實現中,過濾器充當 Spring Security Core 調用者的身份,通常流程爲:架構

  • 過濾器提取請求中的認證信息封裝爲 Authentication 傳遞給 AuthenticationManager 進行認證,而後將認證結果放到 SecurityContext 中供後續過濾器使用
  • 過濾器在請求進入端點前根據認證結果利用 AccessDecisionManager 判斷是否具有相應的權限

在這裏,Spring Security Core 只是 Spring Security Web 利用的一部分功能,更爲重要的是,整個過濾器鏈。

過濾器鏈的構建<a id="sec-3-1"></a>

以前原本只是想了解一下過濾器鏈的調用過程,可是看着看着,就跑到源碼去了。反應過來的時候才發現,已經搞了這麼多了停下來的話有點吃虧,就乾脆把過濾器鏈的構建邏輯理了一下。

<details><summary><i></i></summary>

在梳理完構建器鏈的構建和調用邏輯後感受,過濾器鏈的構建邏輯貌似沒有好多用,還不如直接看過濾器鏈的調用邏輯……

</details>

這部分邏輯的梳理過程有些複雜,反正我調試的時候斷點就在 build() 方法附近反覆橫跳,這裏爲了簡單,就直接放結果了<sup><a id="fnr.1" class="footref" href="#fn.1">1</a></sup>:

<img src="https://user-gold-cdn.xitu.io/2019/11/2/16e2c58c73711fa5?w=1125&h=777&f=png&s=86478">

時序圖畫的不是很標準,大體意思一下就能夠了哈( ̄▽ ̄),解析以下:

  1. Spring Security Web 中的過濾器鏈的構建主要是由 WebSecurityHttpSecurity 完成的
  2. WebSecurity 根據上下文中的 WebSecurityConfigurer 構建出 HttpSecurity 對象,而後經過 HttpSecurity 構建出 SecurityFilterChain 後,將 SecurityFilterChain 放到 FilterChainProxy 中。 其中,WebSecurityConfigurer 的經常使用實現爲 WebMvcConfigurerAdapter, 而 SecurityFilterChain 的經常使用實現爲 DefaultSecurityFilterChain
  3. HttpSecurity 根據直接添加的 Filter 和經過 AbstractHttpConfigurer 實現類構建的 Filter 生成過濾器鏈

這部分邏輯中,關鍵的對象分別是 WebSecurity 和它依賴的配置類 WebSecurityConfigurer, HttpSecurity 和它依賴的配置類 AbstractHttpConfigurer.

在實際的使用中,咱們一般會繼承 WebMvcConfigurerAdapter 這個 WebSecurityConfigurer 的實現類,而後在重寫它的 configure(HttpSecurity) 方法:

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    public void configure(HttpSecurity http) throws Exception {
        // @formatter:off
        http
            .authorizeRequests()
              .antMatchers("/oauth/**")
              .authenticated()
              .and()
            .requestMatchers()
              .antMatchers("/oauth/**","/login/**","/logout/**")
              .and()
            .csrf()
              .disable()
            .formLogin()
              .permitAll();
        // @formatter:on
    }
}

在上面這個類中,咱們繼承了 WebSecurityConfigurerAdapter 這個類,當咱們將自定義的類放到 Spring 上下文中後,就能夠被 WebSecurity 拿到用於構建 HttpSecurity, 而重寫的 configure(HttpSecurity) 則會在 HttpSecurity 構建過濾器以前調用,完成過濾器鏈的配置。

其中,諸如 csrf() 之類的方法都會返回一個 AbstractHttpConfigurer 實現,容許咱們對特定的過濾器進行配置。

到了最後,HttpSecurity 就能夠根據相應的配置完成過濾器鏈的構建,而後再由 WebSecurity 將它們放到 FilterChainProxy 實例中返回。

過濾器鏈的調用<a id="sec-3-2"></a>

過濾器鏈的調用的話,主要涉及兩個對象:FilterChainProxy 和 DefaultSecurityFilterChain,關鍵其實仍是在 FilterChainProxy 上。

然而,這兩個對象的源碼都挺簡單的,這裏就不貼了,有興趣的能夠去看一下,這裏簡單說一下結果:

  • FilterChainProxy 會做爲 Servlet 容器過濾器鏈中的一個過濾器,當接收到請求後在持有的過濾器鏈中判斷是否存在匹配的過濾器鏈
  • 存在匹配的過濾器鏈時,會直接使用第一個匹配項對請求進行處理
  • 不存在匹配的過濾器鏈或者匹配的過濾器鏈走完後,就會回到 Servlet 容器過濾器鏈繼續執行

這裏的關鍵點其實就是,存在多條過濾器鏈,每條過濾器鏈匹配必定的請求。以前看文檔的時候不仔細,沒有意識到這一點,饒了很多彎路 QAQ

附圖:

<img src="https://user-gold-cdn.xitu.io/2019/11/2/16e2c58cc7967f3e?w=1190&h=839&f=png&s=65564">

過濾器鏈的使用<a id="sec-3-3"></a>

Spring Security Web 過濾器的使用主要就是自定義過濾器鏈,默認的過濾器鏈會添加一些 Spring Security Web 自帶的一些過濾器,使用時,須要考慮是否去掉默認的一些過濾器器(或者不使用默認配置), 並將自定義的過濾器添加到過濾器鏈中的一個合適的位置上。

這裏會簡要介紹部份內置過濾器的做用和過濾器的順序,首先是內置的幾個過濾器:

  • 過濾器 SecurityContextPersistenceFilter 能夠從 Session 中取出已認證用戶的信息
  • 過濾器 AnonymousAuthenticationFilter 在發現 SecurityContextHolder 中尚未認證信息時,會生成一個匿名認證信息放到 SecurityContextHolder
  • 過濾器 ExceptionTranslationFilter 能夠處理 FilterSecurityInterceptor 中拋出的異常,進行重定向、輸出錯誤信息等
  • 過濾器 FilterSecurityInterceptor 對認證信息的權限進行判斷,權限不足時拋出異常

在自定義過濾器時(一般是認證過濾器),咱們須要考慮自定義過濾器的位置,好比,咱們不該該把自定義的認證過濾器放在 AnonymousAuthenticationFilter 的後面,官方文檔對過濾器的順序給出瞭解釋: 在去除一些過濾器後,大體順序就爲:

<img src="https://user-gold-cdn.xitu.io/2019/11/3/16e2f52abed85b6f?w=247&h=269&f=png&s=17760">

其中,AuthenticationProcessingFilter 是指認證過濾器實現,好比經常使用的 UsernamePasswordAuthenticationFilter 這個過濾器。

完整的順序能夠參考:

在這個順序中,因爲 SecurityContextPersistenceFilter 可能從 Session 中取出已認證用戶的信息,所以,自定義過濾器時應該考慮 SecurityContextHolder 是否是已經存在用戶認證信息。 或者在登陸/註冊相關 URL 的過濾器鏈中設置認證用戶帳戶密碼的過濾器,在其餘過濾器鏈中設置認證 token 的過濾器。

Spring Security OAuth2<a id="sec-4"></a>

Spring Security OAuth2 創建在 Spring Security Core 和 Spring Security Web 的基礎上,提供了對 OAuth2 受權框架的支持。

其中,最爲複雜的部分是在 受權服務器 上,相對的,資源服務器基本上就是重用 Spring Security Web 提供的過濾器鏈,經過過濾器 OAuth2AuthenticationProcessingFilter 和請求攜帶的 Token 獲取認證信息, 所以,這裏的重心會放在受權服務器上。

受權服務器<a id="sec-4-1"></a>

對於傳統的認證方式來講,簡單認證用戶的信息基本上就足夠了,可是對於 OAuth2 來講是不夠的,對於 OAuth2 受權服務器來講,除了須要完成用戶的認證之外,還需完成客戶端的認證,還須要效驗客戶端請求的 Scope, 所以,單憑過濾器鏈是不足以完成二者的認證的,由於 SecurityContextHolder 只能持有一個認證結果。

因而,Spring Security OAuth2 採用的認證策略即是:在過濾器鏈中完成客戶端或用戶的認證,而後再在端點的內部邏輯中完成剩餘信息的效驗。而這個認證策略,在不一樣模式中也是不同的。

這裏主要會對 受權碼模式密碼模式 中的認證策略進行介紹,由於這兩個模式中使用到的端點 AuthorizationEndpointTokenEndpoint 已經涵蓋了兩條主要的過濾器鏈。

受權碼模式<a id="sec-4-1-1"></a>

首先是受權碼模式,對於受權碼模式來講,請求流程一般是先到 /oauth/authorize 獲取受權碼,而後再到 /oauth/token 獲取 Token,對於 /oauth/authorize 這個端點的過濾器鏈來講, 認證的是用戶的信息,認證經過後進入端點內部,會對客戶端請求 Scope 和用戶的 Approval 進行效驗,效驗經過會生成受權碼返回給客戶端。

其實這裏也就能夠明白爲何 /oauth/authorize 這個端點須要對用戶進行認證了,由於,這裏須要獲取的是 用戶 的受權。

而後客戶端拿着受權碼去 /oauth/token 這個端點獲取 Token 時,該端點的過濾器鏈會對客戶端進行認證,認證經過後進入端點內部,這時端點內部會對客戶端請求的 Scope 進行效驗, 效驗經過後就會經過 TokenGranter 生成 Token 返回給客戶端。

也就是說,對於受權碼模式來講:

  • 端點 /oauth/authorize 完成用戶的認證、客戶端請求的 Scope 的效驗、用戶的受權檢查
  • 端點 /oauth/token 完成客戶端的認證,客戶端請求的 Scope 的效驗、客戶端受權碼的檢查

這其實就能夠看作時對受權碼模式的代碼解釋,由於,在受權碼模式中,去獲取 Token 的每每不是用戶操做的客戶端,所以,須要認證客戶端是不是受信任的。

相關邏輯對應的源碼,去掉了一部分效驗代碼:

@RequestMapping(value = "/oauth/authorize")
public ModelAndView authorize(Map<String, Object> model, @RequestParam Map<String, String> parameters, SessionStatus sessionStatus, Principal principal) {
  AuthorizationRequest authorizationRequest = getOAuth2RequestFactory().createAuthorizationRequest(parameters);

  try {
    // 未經過認證的請求會拋異常
    if (!(principal instanceof Authentication) || !((Authentication) principal).isAuthenticated()) {
      throw new InsufficientAuthenticationException("User must be authenticated with Spring Security before authorization can be completed.");
    }

    ClientDetails client = getClientDetailsService().loadClientByClientId(authorizationRequest.getClientId());

    // 效驗 Scope
    oauth2RequestValidator.validateScope(authorizationRequest, client);

    // 效驗用戶的受權
    authorizationRequest = userApprovalHandler.checkForPreApproval(authorizationRequest, (Authentication) principal);
    boolean approved = userApprovalHandler.isApproved(authorizationRequest, (Authentication) principal);
    authorizationRequest.setApproved(approved);

    // Validation is all done, so we can check for auto approval...
    if (authorizationRequest.isApproved()) {
      if (responseTypes.contains("token")) {
        return getImplicitGrantResponse(authorizationRequest);
      }
      if (responseTypes.contains("code")) {
        return new ModelAndView(getAuthorizationCodeResponse(authorizationRequest, (Authentication) principal));
      }
    }

    return getUserApprovalPageResponse(model, authorizationRequest, (Authentication) principal);
  }
  catch (RuntimeException e) {
    sessionStatus.setComplete();
    throw e;
  }
}

@RequestMapping(value = "/oauth/token", method=RequestMethod.POST)
public ResponseEntity<OAuth2AccessToken> postAccessToken(Principal principal, @RequestParam Map<String, String> parameters)
  throws HttpRequestMethodNotSupportedException {

  // 能夠看到,經過效驗的是客戶端
  String clientId = getClientId(principal);
  ClientDetails authenticatedClient = getClientDetailsService().loadClientByClientId(clientId);

  TokenRequest tokenRequest = getOAuth2RequestFactory().createTokenRequest(parameters, authenticatedClient);

  // 效驗請求的 Scope
  if (authenticatedClient != null) {
    oAuth2RequestValidator.validateScope(tokenRequest, authenticatedClient);
  }

  if (isAuthCodeRequest(parameters)) {
    // The scope was requested or determined during the authorization step
    if (!tokenRequest.getScope().isEmpty()) {
      tokenRequest.setScope(Collections.<String> emptySet());
    }
  }

  // 調用 TokenGranter 進行受權
  OAuth2AccessToken token = getTokenGranter().grant(tokenRequest.getGrantType(), tokenRequest);
  if (token == null) {
    throw new UnsupportedGrantTypeException("Unsupported grant type: " + tokenRequest.getGrantType());
  }
  return getResponse(token);
}

受權碼模式流程圖:

<img src="https://user-gold-cdn.xitu.io/2019/11/2/16e2c58c6bb340ca?w=710&h=406&f=png&s=30973">

密碼模式<a id="sec-4-1-2"></a>

密碼模式,或者說簡化模式,只有一個端點即 /oauth/token 這個端點,也就是說,這個端點要同時完成用戶和客戶端的認證。

可是,這個端點不可能同時擁有兩個過濾器鏈,而爲了支持受權碼模式,這個端點的過濾器鏈的職責已經肯定了,就是完成客戶端的認證。所以,用戶的認證就只能在端點內部邏輯完成。

TokenEndpoint 發現受權模式爲 密碼模式 時,會將 ResourceOwnerPasswordTokenGranter 放入 TokenGranter, 而 ResourceOwnerPasswordTokenGranter 進行受權時會調用 AuthenticationManager 來完成對用戶的認證,認證成功纔會經過 TokenService 生成 Token 返回。

// AuthorizationServerEndpointsConfigurer.getDefaultTokenGranters
private List<TokenGranter> getDefaultTokenGranters() {
  List<TokenGranter> tokenGranters = new ArrayList<TokenGranter>();
  tokenGranters.add(new AuthorizationCodeTokenGranter(tokenServices, authorizationCodeServices, clientDetails, requestFactory));
  tokenGranters.add(new RefreshTokenGranter(tokenServices, clientDetails, requestFactory));
  tokenGranters.add(new ImplicitTokenGranter(tokenServices, clientDetails, requestFactory));
  tokenGranters.add(new ClientCredentialsTokenGranter(tokenServices, clientDetails, requestFactory));
  if (authenticationManager != null) {
    tokenGranters.add(new ResourceOwnerPasswordTokenGranter(authenticationManager, tokenServices, clientDetails, requestFactory));
  }
  return tokenGranters;
}

密碼模式流程圖:

<img src="https://user-gold-cdn.xitu.io/2019/11/2/16e2c58cc7efb8fe?w=700&h=504&f=png&s=35288">

客戶端認證<a id="sec-4-1-3"></a>

經過對 受權碼模式密碼模式 的瞭解咱們知道了客戶端的認證是在過濾器鏈中完成的,這個認證能夠經過 BasicAuthenticationFilter 完成,但更通用的大概是 ClientCredentialsTokenEndpointFilter 這個過濾器。

其內部的認證流程實際上是很簡單的,最爲重要的一點是,它用的仍是 Spring Security Core 那一套!

@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
  throws AuthenticationException, IOException, ServletException {

  String clientId = request.getParameter("client_id");
  String clientSecret = request.getParameter("client_secret");

  // If the request is already authenticated we can assume that this filter is not needed
  Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
  if (authentication != null && authentication.isAuthenticated()) {
    return authentication;
  }

  UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(clientId, clientSecret);

  // 經過 AuthenticationManager 完成認證
  return this.getAuthenticationManager().authenticate(authRequest);
}

咱們知道,Spring Security OAuth2 提供了 ClientDetails 和 ClientDetailsService 這兩種抽象,它們和 UserDetails 和 UserDetailsService 是不兼容的,這時,能夠選擇本身實現一個 AuthenticationProvider 使用 ClientDetails 和 ClientDetailsService, 但也能夠將 ClientDetails 和 ClientDetailsService 轉換爲 UserDetails 和 UserDetailsService,Spring Security OAuth2 經過 ClientDetailsUserDetailsService 來完成這一轉換:

public class ClientDetailsUserDetailsService implements UserDetailsService {
  private final ClientDetailsService clientDetailsService;

  public ClientDetailsUserDetailsService(ClientDetailsService clientDetailsService) {
    this.clientDetailsService = clientDetailsService;
  }

  public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    ClientDetails clientDetails;
    try {
      clientDetails = clientDetailsService.loadClientByClientId(username);
    } catch (NoSuchClientException e) {
      throw new UsernameNotFoundException(e.getMessage(), e);
    }
    String clientSecret = clientDetails.getClientSecret();
    if (clientSecret== null || clientSecret.trim().length()==0) {
      clientSecret = emptyPassword;
    }
    return new User(username, clientSecret, clientDetails.getAuthorities());
  }
}

TokenGranter<a id="sec-4-1-4"></a>

Spring Security OAuth2 中受權碼的生成時經過 TokenGranter 來完成的,進行受權碼的生成時,會遍歷擁有的各個 TokenGranter 實現,直到成功生成 Token 或者全部 TokenGranter 實現都不能生成 Token。

生成 Token 也是一個能夠抽象出來的環節,所以,Spring Security OAuth2 經過 TokenService 和 TokenStore 來生成、獲取和保存 Token。

public abstract class AbstractTokenGranter implements TokenGranter {
  private final AuthorizationServerTokenServices tokenServices;

  private final ClientDetailsService clientDetailsService;

  private final OAuth2RequestFactory requestFactory;

  private final String grantType;

  protected AbstractTokenGranter(AuthorizationServerTokenServices tokenServices,
      ClientDetailsService clientDetailsService, OAuth2RequestFactory requestFactory, String grantType) {
    this.clientDetailsService = clientDetailsService;
    this.grantType = grantType;
    this.tokenServices = tokenServices;
    this.requestFactory = requestFactory;
  }

  public OAuth2AccessToken grant(String grantType, TokenRequest tokenRequest) {
    // 每一個 TokenGranter 對應一種受權類型
    if (!this.grantType.equals(grantType)) {
      return null;
    }

    String clientId = tokenRequest.getClientId();
    ClientDetails client = clientDetailsService.loadClientByClientId(clientId);
    validateGrantType(grantType, client);

    // 獲取受權碼
    return getAccessToken(client, tokenRequest);
  }

  protected OAuth2AccessToken getAccessToken(ClientDetails client, TokenRequest tokenRequest) {
    return tokenServices.createAccessToken(getOAuth2Authentication(client, tokenRequest));
  }
}

// 默認的 TokenServices 的部分代碼
public class DefaultTokenServices {
  @Transactional
  public OAuth2AccessToken createAccessToken(OAuth2Authentication authentication) throws AuthenticationException {
    // 首先從 TokenStore 中獲取 Token
    OAuth2AccessToken existingAccessToken = tokenStore.getAccessToken(authentication);
    OAuth2RefreshToken refreshToken = null;
    if (existingAccessToken != null) {
      if (existingAccessToken.isExpired()) {
        if (existingAccessToken.getRefreshToken() != null) {
          refreshToken = existingAccessToken.getRefreshToken();
          tokenStore.removeRefreshToken(refreshToken);
        }
        tokenStore.removeAccessToken(existingAccessToken);
      }
      else {
        // Re-store the access token in case the authentication has changed
        tokenStore.storeAccessToken(existingAccessToken, authentication);
        return existingAccessToken;
      }
    }

    if (refreshToken == null) {
      refreshToken = createRefreshToken(authentication);
    }

    OAuth2AccessToken accessToken = createAccessToken(authentication, refreshToken);
    // 保存 accessToken
    tokenStore.storeAccessToken(accessToken, authentication);
    refreshToken = accessToken.getRefreshToken();
    if (refreshToken != null) {
      tokenStore.storeRefreshToken(refreshToken, authentication);
    }
    return accessToken;
  }

  // 從 TokenStore 中獲取 Token
  public OAuth2AccessToken getAccessToken(OAuth2Authentication authentication) {
    return tokenStore.getAccessToken(authentication);
  }
}

簡單來講就是:

  1. 在過濾器鏈和端點內部邏輯中完成客戶端和用戶的認證與 Scope 的效驗
  2. 經過 TokenGranter 生成 Token,而 TokenGranter 經過 TokenService 建立 Token,TokenStore 能夠保存 Token

資源服務器<a id="sec-4-2"></a>

資源服務器相較於受權服務器來講就要簡單多了,和傳統的流程差很少,經過過濾器 OAuth2AuthenticationProcessingFilterOAuth2AuthenticationManager 驗證 Token 並獲取認證信息:

public class OAuth2AuthenticationProcessingFilter implements Filter, InitializingBean {
  public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
    final HttpServletRequest request = (HttpServletRequest) req;
    final HttpServletResponse response = (HttpServletResponse) res;

    // 從請求頭中提取 Token
    Authentication authentication = tokenExtractor.extract(request);

    Authentication authResult = authenticationManager.authenticate(authentication);

    SecurityContextHolder.getContext().setAuthentication(authResult);

    chain.doFilter(request, response);
  }
}

public class OAuth2AuthenticationManager implements AuthenticationManager, InitializingBean {
  public Authentication authenticate(Authentication authentication) throws AuthenticationException {
    String token = (String) authentication.getPrincipal();

    // 經過 TokenService 獲取認證信息
    OAuth2Authentication auth = tokenServices.loadAuthentication(token);

    if (auth == null) {
      throw new InvalidTokenException("Invalid token: " + token);
    }

    checkClientDetails(auth);

    if (authentication.getDetails() instanceof OAuth2AuthenticationDetails) {
      OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails) authentication.getDetails();
      // Guard against a cached copy of the same details
      if (!details.equals(auth.getDetails())) {
        // Preserve the authentication details from the one loaded by token services
        details.setDecodedDetails(auth.getDetails());
      }
    }
    auth.setDetails(authentication.getDetails());
    auth.setAuthenticated(true);
    return auth;
  }
}

Spring Security JWT<a id="sec-5"></a>

不少地方均可以看到 JWT 在 OAuth2 中的使用,Spring Security JWT 在 Spring Security OAuth2 中便扮演了 TokenService 和 TokenStore 的角色,用於生成和效驗 Token。

可是,我仍是很想吐槽一下 JWT 這個東西。當初剛看到的時候感受頗有趣,使用 JWT 能夠直接在 Token 中攜帶一些信息,同時服務端還不用存儲 Token 的信息。

然而,在實際的一些使用中,可能會碰見須要做廢還有效的 JWT Token 的需求,這對於 JWT 來講是沒法實現的。爲了實現這一需求,就只能在服務端存儲一些信息。

可是,既然都要在服務端存儲信息了,那幹嗎還用 JWT 呢?只要須要在服務端存儲信息,那麼,用不用 JWT 都沒多大區別了啊……

結語<a id="sec-6"></a>

Spring Security 真的是一個很複雜的框架,目前設計的還只是在 Servlet 程序中的應用,然鵝我目前忽然對 Spring WebFlux 產生了一點興趣, 不知道 Spring Security 在 Spring WebFlux 中是啥樣的……

另外,我想說的是,Spring Security 的官方教程真的很棒,將大致的架構都解釋清楚了,惋惜吃了英語的虧 T<sub>T</sub>

參考連接<a id="sec-7"></a>

Spring Security 總體相關的資料:

Spring Security Web 相關的資料:

Spring Security OAuth2 相關的資料:

Footnotes

<sup><a id="fn.1" href="#fnr.1">1</a></sup> 對詳細過程有興趣的,能夠看個人筆記 Spring Security Web 過濾器鏈的構建

相關文章
相關標籤/搜索