Spring Security OAuth2源码解析(二)

论坛 期权论坛 脚本     
已经匿名di用户   2022-4-13 16:43   1571   0

鉴权服务器对客户端鉴权之后,会生成token,客户端使用token,就可以去资源服务器获取资源。

@EnableResourceServer

@Import(ResourceServerConfiguration.class)
public @interface EnableResourceServer {

}

ResourceServerConfiguration

ResourceServerConfigurationWebSecurityConfigurerAdapter的子类实现。引入了ResourceServerSecurityConfigurerResourceServerTokenServices

@Override
 protected void configure(HttpSecurity http) throws Exception {
    //引入Filter配置
  ResourceServerSecurityConfigurer resources = new ResourceServerSecurityConfigurer();
        //引入ResourceServerTokenServices
  ResourceServerTokenServices services = resolveTokenServices();
  if (services != null) {
   resources.tokenServices(services);
  }
  else {
   if (tokenStore != null) {
    resources.tokenStore(tokenStore);
   }
   else if (endpoints != null) {
    resources.tokenStore(endpoints.getEndpointsConfigurer().getTokenStore());
   }
  }
  if (eventPublisher != null) {
   resources.eventPublisher(eventPublisher);
  }
  for (ResourceServerConfigurer configurer : configurers) {
   configurer.configure(resources);
  }
        
        //匿名验证,
  http.authenticationProvider(new AnonymousAuthenticationProvider("default"))
  
  // 异常处理
  .exceptionHandling()
    .accessDeniedHandler(resources.getAccessDeniedHandler()).and()
    .sessionManagement()
    .sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
    .csrf().disable();
  //filter chain
  http.apply(resources);
  if (endpoints != null) {
   // Assume we are in an Authorization Server
   http.requestMatcher(new NotOAuthRequestMatcher(endpoints.oauth2EndpointHandlerMapping()));
  }
  for (ResourceServerConfigurer configurer : configurers) {
   // Delegates can add authorizeRequests() here
   configurer.configure(http);
  }
  if (configurers.isEmpty()) {
            //所有请求需授权
   http.authorizeRequests().anyRequest().authenticated();
  }
 }

ResourceServerSecurityConfigurer

ResourceServerSecurityConfigurer配置了:OAuth2AuthenticationProcessingFilter

@Override
 public void configure(HttpSecurity http) throws Exception {
        //如果是OAuth2AuthenticationManager,则设置tokenService等。
  AuthenticationManager oauthAuthenticationManager = oauthAuthenticationManager(http);
  resourcesServerFilter = new OAuth2AuthenticationProcessingFilter();
  resourcesServerFilter.setAuthenticationEntryPoint(authenticationEntryPoint);
  resourcesServerFilter.setAuthenticationManager(oauthAuthenticationManager);
        
  if (eventPublisher != null) {
   resourcesServerFilter.setAuthenticationEventPublisher(eventPublisher);
  }
  if (tokenExtractor != null) {
   resourcesServerFilter.setTokenExtractor(tokenExtractor);
  }
  if (authenticationDetailsSource != null) {
   resourcesServerFilter.setAuthenticationDetailsSource(authenticationDetailsSource);
  }
  resourcesServerFilter = postProcess(resourcesServerFilter);
  resourcesServerFilter.setStateless(stateless);

  // @formatter:off
  http
   .authorizeRequests().expressionHandler(expressionHandler)
  .and()
        //把OAuth2AuthenticationProcessingFilter 放在 AbstractPreAuthenticatedProcessingFilter 之前。
   .addFilterBefore(resourcesServerFilter, AbstractPreAuthenticatedProcessingFilter.class)
   .exceptionHandling()
    .accessDeniedHandler(accessDeniedHandler)
    .authenticationEntryPoint(authenticationEntryPoint);
  // @formatter:on
 }

OAuth2AuthenticationManager

 private AuthenticationManager oauthAuthenticationManager(HttpSecurity http) {
  OAuth2AuthenticationManager oauthAuthenticationManager = new OAuth2AuthenticationManager();
  if (authenticationManager != null) {
   if (authenticationManager instanceof OAuth2AuthenticationManager) {
    oauthAuthenticationManager = (OAuth2AuthenticationManager) authenticationManager;
   }
   else {
    return authenticationManager;
   }
  }
  oauthAuthenticationManager.setResourceId(resourceId);
  oauthAuthenticationManager.setTokenServices(resourceTokenServices(http));
  oauthAuthenticationManager.setClientDetailsService(clientDetails());
  return oauthAuthenticationManager;
 }

OAuth2AuthenticationProcessingFilter

 public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException,
   ServletException {

  final boolean debug = logger.isDebugEnabled();
  final HttpServletRequest request = (HttpServletRequest) req;
  final HttpServletResponse response = (HttpServletResponse) res;

  try {
            //提取Authentication
   Authentication authentication = tokenExtractor.extract(request);
   //没有验证,
   if (authentication == null) {
    if (stateless && isAuthenticated()) {
     if (debug) {
      logger.debug("Clearing security context.");
     }
     SecurityContextHolder.clearContext();
    }
    if (debug) {
     logger.debug("No token in request, will continue chain.");
    }
   }
   else {
                //设置ACCESS_TOKEN_VALUE到request,
    request.setAttribute(OAuth2AuthenticationDetails.ACCESS_TOKEN_VALUE, authentication.getPrincipal());
    if (authentication instanceof AbstractAuthenticationToken) {
     AbstractAuthenticationToken needsDetails = (AbstractAuthenticationToken) authentication;
     needsDetails.setDetails(authenticationDetailsSource.buildDetails(request));
    }
                //验证, authenticationManager 是 OAuth2AuthenticationManager
    Authentication authResult = authenticationManager.authenticate(authentication);

    if (debug) {
     logger.debug("Authentication success: " + authResult);
    }

    eventPublisher.publishAuthenticationSuccess(authResult);
    SecurityContextHolder.getContext().setAuthentication(authResult);

   }
  }
  catch (OAuth2Exception failed) {
   SecurityContextHolder.clearContext();
........
   return;
  }

  chain.doFilter(request, response);
 }

TokenExtractor

从请求中提取Authentication

public interface TokenExtractor {
 Authentication extract(HttpServletRequest request);

}

BearerTokenExtractor

TokenExtractor 的默认实现,Bearer方式,从header中提取。

protected String extractHeaderToken(HttpServletRequest request) {
  Enumeration<String> headers = request.getHeaders("Authorization");
  while (headers.hasMoreElements()) { // typically there is only one (most servers enforce that)
   String value = headers.nextElement();
//Bearer
   if ((value.toLowerCase().startsWith(OAuth2AccessToken.BEARER_TYPE.toLowerCase()))) {
    String authHeaderValue = value.substring(OAuth2AccessToken.BEARER_TYPE.length()).trim();
    // Add this here for the auth details later. Would be better to change the signature of this method.
    request.setAttribute(OAuth2AuthenticationDetails.ACCESS_TOKEN_TYPE,
      value.substring(0, OAuth2AccessToken.BEARER_TYPE.length()).trim());
    int commaIndex = authHeaderValue.indexOf(',');
    if (commaIndex > 0) {
     authHeaderValue = authHeaderValue.substring(0, commaIndex);
    }
    return authHeaderValue;
   }
  }

  return null;
 }

OAuth2AuthenticationManager

 public Authentication authenticate(Authentication authentication) throws AuthenticationException {

  if (authentication == null) {
   throw new InvalidTokenException("Invalid token (token not found)");
  }
        //获取token,默认是一个UUID,
  String token = (String) authentication.getPrincipal();
        //根据token获取Authentication
  OAuth2Authentication auth = tokenServices.loadAuthentication(token);
  if (auth == null) {
   throw new InvalidTokenException("Invalid token: " + token);
  }
        //资源列表
  Collection<String> resourceIds = auth.getOAuth2Request().getResourceIds();
  if (resourceId != null && resourceIds != null && !resourceIds.isEmpty() && !resourceIds.contains(resourceId)) {
   throw new OAuth2AccessDeniedException("Invalid token does not contain resource id (" + resourceId + ")");
  }
        //检测客户端信息
  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;

 }

ResourceServerTokenServices

ResourceServerTokenServices实现加载Authentication和读取token的功能。

public interface ResourceServerTokenServices {
 OAuth2Authentication loadAuthentication(String accessToken) throws AuthenticationException, InvalidTokenException;
 OAuth2AccessToken readAccessToken(String accessToken);
}

流程:

图从其他地方copy的。

https://img2018.cnblogs.com/blog/1772687/201909/1772687-20190925151556531-500611638.jpg

分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:81
帖子:4969
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP