# 概述

Oauth2是让第三方应用用户授权的情况下,通过认证服务器的认证,从而安全的在资源服务器获得对应的用户资源流程指导

# Oauth2的流程

     +--------+                               +---------------+
     |        |--(A)- Authorization Request ->|   Resource    |
     |        |                               |     Owner     |
     |        |<-(B)-- Authorization Grant ---|               |
     |        |                               +---------------+
     |        |
     |        |                               +---------------+
     |        |--(C)-- Authorization Grant -->| Authorization |
     | Client |                               |     Server    |
     |        |<-(D)----- Access Token -------|               |
     |        |                               +---------------+
     |        |
     |        |                               +---------------+
     |        |--(E)----- Access Token ------>|    Resource   |
     |        |                               |     Server    |
     |        |<-(F)--- Protected Resource ---|               |
     +--------+                               +---------------+
  1. 第三方应用向资源持有者请求资源
  2. 资源持有者授权给予第三方应用一个许可
  3. 第三方应用将该许可给予认证服务器进行认证,认证成功则返回一个AccessToken
  4. 第三分应用根据AccessToken资源服务器,获取对应的资源 在上面的流程中,第二步,资源持有者如何授权给予第三方应用许可,是最关键的地方

# 四种权限授予方式

参考文章Oauth2.0 ( RFC-6749 ) 中文译文 (opens new window)

  • 授权码模式(Authorization Code) 授权码是通过授权服务器获取的,授权服务器是客户端和资源拥有者之间的媒介。与客户端直接向资源拥有者申请权限不同,客户端通过将资源拥有者引向授权服务器,然后授权服务器反过来将资源拥有者redirect到客户端,并带上授权码
    授权服务器在将资源拥有者redirect到客户端(并带上授权码)之前,会验证资源拥有者,并获取资源拥有者的授权。因为资源拥有者仅与授权服务器进行身份认证,所以资源拥有者的凭证(用户名,密码等)永远不会泄露给第三方客户端。
    授权码模式有一些重要的安全优势,比如验证客户端的能力比如直接将accessToken传送给客户端而不是通过资源拥有者传送给客户端。

  • 简化模式(implicit) 简化模式是为在浏览器中,使用诸如javaScript之类的脚本语言而优化的一种简化的授权码流程。简化模式中,直接将accessToken而不是authorizationCode颁发给客户端。 简化模式中颁发accessToken时,授权服务器没有对客户端进行验证。

  • 密码模式(resource owner password credentials) 资源拥有者密码凭据,直接用来当作一种获取accessToken的权限授予方式。

  • 客户端模式 客户端凭据用作权限授予。

# 源码

# oauth2集成逻辑设计

@Import({AuthorizationServerEndpointsConfiguration.class, AuthorizationServerSecurityConfiguration.class})
public @interface EnableAuthorizationServer {
}
@Configuration
@Order(0)
@Import({ ClientDetailsServiceConfiguration.class, AuthorizationServerEndpointsConfiguration.class })
public class AuthorizationServerSecurityConfiguration extends WebSecurityConfigurerAdapter {
    @Autowired
    /*xxx: 注入所有的 授权服务配置器*/
    private List<AuthorizationServerConfigurer> configurers = Collections.emptyList();

    @Autowired
    /*xxx: 客户端详情信息服务*/
    private ClientDetailsService clientDetailsService;

    @Autowired
    /*xxx: 授权服务器服务端口配置*/
    private AuthorizationServerEndpointsConfiguration endpoints;

    @Autowired
    /*xxx: 客户端服务配置器对授权服务配置器进行配置*/
    public void configure(ClientDetailsServiceConfigurer clientDetails) throws Exception {
        for (AuthorizationServerConfigurer configurer : configurers) {
            configurer.configure(clientDetails);
        }
    }

    @Override
    /*xxx: 单条过滤链自定义配置*/
    protected void configure(HttpSecurity http) throws Exception {
        /*xxx: 授权服务器*/
        AuthorizationServerSecurityConfigurer configurer = new AuthorizationServerSecurityConfigurer();

        /*xxx: 对授权服务器进行配置*/
        configure(configurer);

        /*xxx: 进行配置收集(此处是配置单条过滤链)*/
        http.apply(configurer);

        http
                /*xxx: 进行授权配置*/
                .authorizeRequests()
                /*xxx: 获取授权码,需要进行完全授权*/
                .antMatchers("/oauth/token").fullyAuthenticated()
                /*xxx: 根据配置的表达式决定是否放行*/
                .antMatchers("/oauth/token_key").access(configurer.getTokenKeyAccess())
                /*xxx: 根据配置的表达式决定是否放行*/
                .antMatchers("/oauth/check_token").access(configurer.getCheckTokenAccess())
        .and()
                /*xxx: 过滤链匹配逻辑配置,只有以上配置的三个端口匹配时,才进入本次过滤链*/
                .requestMatchers()
                .antMatchers("/oauth/token", "/oauth/token_key", "/oauth/check_token")
        .and()
                /*xxx: 配置不创建session*/
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.NEVER);

        /*xxx: 共享客户端服务实例*/
        http.setSharedObject(ClientDetailsService.class, clientDetailsService);
    }

    /*xxx: 配置单条过滤链*/
    protected void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
        for (AuthorizationServerConfigurer configurer : configurers) {
            configurer.configure(oauthServer);
        }
    }
}

# oauth2配置项设计

@Import(TokenKeyEndpointRegistrar.class)
/*xxx: 授权服务器端点配置*/
public class AuthorizationServerEndpointsConfiguration {
    @Bean
	/*xxx: 授权端口配置: /oauth/authorize
	   授权通过的地址:/oauth/confirm_access
	   授权失败的地址:/oauth/error
	   */
    public AuthorizationEndpoint authorizationEndpoint() throws Exception {
        /*xxx: 该端口具有两个用于授权的控制器,一个用于返回用户确认授权的ui,一个用于授权后的重定向*/
        AuthorizationEndpoint authorizationEndpoint = new AuthorizationEndpoint();

        /*xxx: 授权通过地址*/
        authorizationEndpoint.setUserApprovalPage(extractPath(mapping, "/oauth/confirm_access"));

        /*xxx: 授权失败地址*/
        authorizationEndpoint.setErrorPage(extractPath(mapping, "/oauth/error"));
        /*xxx: 令牌授权颁发器*/
        authorizationEndpoint.setTokenGranter(tokenGranter());
        /*xxx: 客户端服务*/
        authorizationEndpoint.setClientDetailsService(clientDetailsService);
        /*xxx: 授权码服务*/
        authorizationEndpoint.setAuthorizationCodeServices(authorizationCodeServices());
        /*xxx: oauth2请求工程*/
        authorizationEndpoint.setOAuth2RequestFactory(oauth2RequestFactory());
        /*xxx: oauth2请求验证器*/
        authorizationEndpoint.setOAuth2RequestValidator(oauth2RequestValidator());

        return authorizationEndpoint;
    }

    @Bean
    /*xxx: 获取授权码端口配置: /oauth/token */
    public TokenEndpoint tokenEndpoint() throws Exception {
        TokenEndpoint tokenEndpoint = new TokenEndpoint();
        /*xxx: 省略其它抽象...*/
        return tokenEndpoint;
    }

    @Bean
    /*xxx: 查验令牌端口: /oauth/check_token*/
    public CheckTokenEndpoint checkTokenEndpoint() {
        CheckTokenEndpoint endpoint = new CheckTokenEndpoint(getEndpointsConfigurer().getResourceServerTokenServices());

        /*xxx: 设置accessToken转换器*/
        endpoint.setAccessTokenConverter(getEndpointsConfigurer().getAccessTokenConverter());

        return endpoint;
    }
}
@Component
/*xxx: 默认注册了jwt的令牌端口*/
protected static class TokenKeyEndpointRegistrar implements BeanDefinitionRegistryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        /*xxx: 如果上下文中,存在 jwtAccessToken转换器,就对jwt令牌端口进行注册 */
        String[] names = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory,
                JwtAccessTokenConverter.class, false, false);
        if (names.length > 0) {
            /*xxx: 将 jwt的令牌端口,注册到 容器中 */
            BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(TokenKeyEndpoint.class);
            builder.addConstructorArgReference(names[0]);
            registry.registerBeanDefinition(TokenKeyEndpoint.class.getName(), builder.getBeanDefinition());
        }
    }
}
/*xxx: jwt令牌端口*/
@FrameworkEndpoint
public class TokenKeyEndpoint {
    /*xxx: jwt令牌转换器*/
    private JwtAccessTokenConverter converter;

    @RequestMapping(value = "/oauth/token_key", method = RequestMethod.GET)
    @ResponseBody
    /*xxx: 获取令牌信息*/
    public Map<String, String> getKey(Principal principal) {
        /*xxx: 从converter中获取相关信息*/
        Map<String, String> result = converter.getKey();
        return result;
    }
    
    
}
@Configuration
public class ClientDetailsServiceConfiguration {
    private ClientDetailsServiceConfigurer configurer = new ClientDetailsServiceConfigurer(new ClientDetailsServiceBuilder());

    @Bean
    @Lazy
    @Scope(proxyMode=ScopedProxyMode.INTERFACES)
    public ClientDetailsService clientDetailsService() throws Exception {
        return configurer.and().build();
    }
}
public class ClientDetailsServiceConfigurer extends
        SecurityConfigurerAdapter<ClientDetailsService, ClientDetailsServiceBuilder<?>> {
    @Override
    public void init(ClientDetailsServiceBuilder<?> builder) throws Exception {
    }

    @Override
    public void configure(ClientDetailsServiceBuilder<?> builder) throws Exception {
    }

    /*xxx: 内存数据库*/
    public InMemoryClientDetailsServiceBuilder inMemory() throws Exception {
        InMemoryClientDetailsServiceBuilder next = getBuilder().inMemory();
        setBuilder(next);
        return next;
    }
    /*xxx: jdbc数据库*/
    public JdbcClientDetailsServiceBuilder jdbc(DataSource dataSource) throws Exception {
        JdbcClientDetailsServiceBuilder next = getBuilder().jdbc().dataSource(dataSource);
        setBuilder(next);
        return next;
    }
}

# oauth2抽象层设计

# 抽象端口逻辑设计

/*xxx: 抽象端点*/
public class AbstractEndpoint implements InitializingBean {
  /*xxx: 令牌颁发器*/
  private TokenGranter tokenGranter;

  /*xxx: 客户端服务*/
  private ClientDetailsService clientDetailsService;

  /*xxx: oauth2请求工厂*/
  private OAuth2RequestFactory oAuth2RequestFactory;

  /*xxx: oauth2默认工厂*/
  private OAuth2RequestFactory defaultOAuth2RequestFactory;

  public void afterPropertiesSet() throws Exception {
      /*xxx: oauth2请求工厂 依赖客户端服务*/
    defaultOAuth2RequestFactory = new DefaultOAuth2RequestFactory(getClientDetailsService());
  }
}

# 令牌颁发器--逻辑设计

/*xxx: 令牌颁发器*/
public interface TokenGranter {

	/*xxx: 颁发访问令牌*/
	/*xxx: 参数:授权类型,令牌请求实例*/
	OAuth2AccessToken grant(String grantType, TokenRequest tokenRequest);
}
public abstract class AbstractTokenGranter implements TokenGranter {
  /*xxx: 授权服务器令牌服务*/
  private final AuthorizationServerTokenServices tokenServices;
    
  /*xxx: 颁发令牌 */
  public OAuth2AccessToken grant(String grantType, TokenRequest tokenRequest) {
    String clientId = tokenRequest.getClientId();
    ClientDetails client = clientDetailsService.loadClientByClientId(clientId);
    /*xxx: 验证客户端,针对当前授权模式的合法性*/
    validateGrantType(grantType, client);

    return getAccessToken(client, tokenRequest);
  }

  /*xxx: 获取访问令牌 */
  protected OAuth2AccessToken getAccessToken(ClientDetails client, TokenRequest tokenRequest) {
    /*xxx: 通过 令牌服务创建令牌 */
    /*xxx: 根据服务端令牌,创建客户端访问令牌*/
    return tokenServices.createAccessToken(getOAuth2Authentication(client, tokenRequest));
  }

  /*xxx: 除了 客户端授权模式,以及刷新令牌外,其它授权模式,均被重写*/
  /*xxx: 获取认证信息,由子类实现*/
  protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest tokenRequest) {
    OAuth2Request storedOAuth2Request = requestFactory.createOAuth2Request(client, tokenRequest);
    return new OAuth2Authentication(storedOAuth2Request, null);
  }
}

# 授权码模式--令牌颁发器

/*xxx: 授权码模式,令牌颁发器*/
public class AuthorizationCodeTokenGranter extends AbstractTokenGranter {
  /*xxx: 授权码服务*/
  private final AuthorizationCodeServices authorizationCodeServices;
    
  @Override
  /*xxx: 获取oauth2认证信息*/
  protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest tokenRequest) {
    Map<String, String> parameters = tokenRequest.getRequestParameters();
    /*xxx: 从请求中,获取授权码*/
    String authorizationCode = parameters.get("code");

    /*xxx: 通过授权码服务,通过授权码 获取 认证信息*/
    OAuth2Authentication storedAuth = authorizationCodeServices.consumeAuthorizationCode(authorizationCode);

    /*xxx: 验证本次请求的重定向地址,与之前用于请求授权的重定向地址是否一致 */
    if ((redirectUri != null || redirectUriApprovalParameter != null)
            && !pendingOAuth2Request.getRedirectUri().equals(redirectUri)) {
      /*xxx: 不一致,则需要抛错...*/
    }

    /*xxx: 验证本次请求的 clientId,与之前获取认证的 clientId 是否匹配 */
    if (clientId != null && !clientId.equals(pendingClientId)) {
      /*xxx: 不一致,则需要抛错...*/
    }

    /*xxx: 创建认证信息,将用户认证信息,与oauth2流程进行绑定*/
    return new OAuth2Authentication(finalStoredOAuth2Request, userAuth);
  }
}

# 授权码模式--授权码服务

/*xxx: 授权码服务,默认具有两个实现: 内存,以及 jdbc */
public interface AuthorizationCodeServices {
  /*xxx: 生成授权码*/
  String createAuthorizationCode(OAuth2Authentication authentication);

  /*xxx: 消费授权码,返回认证信息*/
  OAuth2Authentication consumeAuthorizationCode(String code)
          throws InvalidGrantException;
}
public abstract class RandomValueAuthorizationCodeServices implements AuthorizationCodeServices {
  /*xxx: 随机数生成器*/
  private RandomValueStringGenerator generator = new RandomValueStringGenerator();

  /*xxx: 存储授权码*/
  protected abstract void store(String code, OAuth2Authentication authentication);

  /*xxx: 移除授权码*/
  protected abstract OAuth2Authentication remove(String code);

  /*xxx: 创建授权码,默认情况下,是根据随机数生成的*/
  public String createAuthorizationCode(OAuth2Authentication authentication) {
    String code = generator.generate();
    /*xxx: 对于生成的授权码,需要对其进行保存*/
    /*xxx: 保存授权码,默认情况下,支持两种: inMemory,inJdbc*/
    store(code, authentication);
    return code;
  }

  public OAuth2Authentication consumeAuthorizationCode(String code)
          throws InvalidGrantException {
    /*xxx: 消费授权码时,需要对其移除*/
    OAuth2Authentication auth = this.remove(code);
    return auth;
  }
}
/*xxx: 内存授权码服务*/
public class InMemoryAuthorizationCodeServices extends RandomValueAuthorizationCodeServices {
  protected final ConcurrentHashMap<String, OAuth2Authentication> authorizationCodeStore = new ConcurrentHashMap<String, OAuth2Authentication>();
}

/*xxx: jdbc授权码服务*/
/*xxx: 保存授权码的表为: oauth_code(code,authentication)*/
public class JdbcAuthorizationCodeServices extends RandomValueAuthorizationCodeServices {
  private final JdbcTemplate jdbcTemplate;
}

# 隐式授权模式--令牌颁发器

/*xxx: 隐式授权码令牌 颁发器*/
public class ImplicitTokenGranter extends AbstractTokenGranter {
  @Override
  protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest clientToken) {

      /*xxx: 直接从上下文中获取认证信息*/
    Authentication userAuth = SecurityContextHolder.getContext().getAuthentication();
    OAuth2Request requestForStorage = ((ImplicitTokenRequest)clientToken).getOAuth2Request();
    return new OAuth2Authentication(requestForStorage, userAuth);
  }
}

# 密码授权模式--令牌颁发器

public class ResourceOwnerPasswordTokenGranter extends AbstractTokenGranter {
  @Override
  protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest tokenRequest) {
    Map<String, String> parameters = new LinkedHashMap<String, String>(tokenRequest.getRequestParameters());
    String username = parameters.get("username");
    String password = parameters.get("password");

    Authentication userAuth = new UsernamePasswordAuthenticationToken(username, password);

    /*xxx: 直接采用原生 springSecurity进行认证*/
    userAuth = authenticationManager.authenticate(userAuth);

    /*xxx: 返回认证信息*/
    OAuth2Request storedOAuth2Request = getRequestFactory().createOAuth2Request(client, tokenRequest);
    return new OAuth2Authentication(storedOAuth2Request, userAuth);
  }
}

# 授权服务器令牌服务--逻辑设计

/*xxx: 授权服务器令牌服务*/
public interface AuthorizationServerTokenServices {
  /*xxx: 创建访问令牌*/
  OAuth2AccessToken createAccessToken(OAuth2Authentication authentication) throws AuthenticationException;

  /*xxx: 刷新访问令牌*/
  OAuth2AccessToken refreshAccessToken(String refreshToken, TokenRequest tokenRequest)
          throws AuthenticationException;

  /*xxx: 获取访问令牌*/
  OAuth2AccessToken getAccessToken(OAuth2Authentication authentication);
}
/*xxx: 默认的令牌服务*/
public class DefaultTokenServices implements AuthorizationServerTokenServices, ResourceServerTokenServices,
		ConsumerTokenServices, InitializingBean {
  /*xxx: 令牌存储器*/
  private TokenStore tokenStore;

  /*xxx: 令牌增强器*/
  private TokenEnhancer accessTokenEnhancer;

  @Transactional
  /*xxx: 创建访问令牌*/
  public OAuth2AccessToken createAccessToken(OAuth2Authentication authentication) throws AuthenticationException {
    /*xxx: 从令牌存储器中,获取之前存在的 访问令牌*/
    OAuth2AccessToken existingAccessToken = tokenStore.getAccessToken(authentication);

    if (existingAccessToken != null) {
      if (existingAccessToken.isExpired()) {
        /*xxx: 如果之前的令牌存在,但是过期了,则进行清理*/
        /*xxx: 同时要把刷新令牌也清理掉*/
        tokenStore.removeRefreshToken(refreshToken);
        tokenStore.removeAccessToken(existingAccessToken);
      }else{
        /*xxx: 没过期,则重新存储令牌,刷新访问时间*/
        tokenStore.storeAccessToken(existingAccessToken, authentication);
        return existingAccessToken;
      }
    }
    /*xxx: 否则*/
    /*xxx: 没有刷新令牌,则创建刷新令牌*/
    /*xxx: 根据刷新令牌,创建 访问令牌*/
    OAuth2AccessToken accessToken = createAccessToken(authentication, refreshToken);
    /*xxx: 最后,将访问令牌以及刷新令牌进行存储*/
    tokenStore.storeAccessToken(accessToken, authentication);
    tokenStore.storeRefreshToken(refreshToken, authentication);
  }

  @Transactional(noRollbackFor={InvalidTokenException.class, InvalidGrantException.class})
  public OAuth2AccessToken refreshAccessToken(String refreshTokenValue, TokenRequest tokenRequest)
          throws AuthenticationException {
      /*xxx: 根据刷新令牌获取访问令牌,略*/
  }

  /*xxx: 获取访问令牌*/
  public OAuth2AccessToken getAccessToken(OAuth2Authentication authentication) {
    return tokenStore.getAccessToken(authentication);
  }

  /*xxx: 创建访问令牌*/
  private OAuth2AccessToken createAccessToken(OAuth2Authentication authentication, OAuth2RefreshToken refreshToken) {
    /*xxx: 通过uuid,创建一个 默认的访问令牌*/
    DefaultOAuth2AccessToken token = new DefaultOAuth2AccessToken(UUID.randomUUID().toString());

    int validitySeconds = getAccessTokenValiditySeconds(authentication.getOAuth2Request());
    if (validitySeconds > 0) {
      token.setExpiration(new Date(System.currentTimeMillis() + (validitySeconds * 1000L)));
    }
    
    token.setRefreshToken(refreshToken);
    token.setScope(authentication.getOAuth2Request().getScope());
    /*xxx: 如果由令牌增强器,则对令牌进行增强*/
    return accessTokenEnhancer != null ? accessTokenEnhancer.enhance(token, authentication) : token;
  }
}

# 令牌存储器--逻辑设计

/*xxx: 令牌存储 */
public interface TokenStore {
  /*xxx: 存储访问令牌*/
  void storeAccessToken(OAuth2AccessToken token, OAuth2Authentication authentication);

  /*xxx: 移除访问令牌*/
  void removeAccessToken(OAuth2AccessToken token);
  
  /*xxx: 省略其它抽象...*/
}

# 内存令牌存储器

/*xxx: 将token访问令牌及其对应的认证信息,存储在内存中, 也就是 concurrentHashMap中*/
public class InMemoryTokenStore implements TokenStore {
  private final ConcurrentHashMap<String, OAuth2AccessToken> accessTokenStore = new ConcurrentHashMap<String, OAuth2AccessToken>();

  private final ConcurrentHashMap<String, OAuth2Authentication> authenticationStore = new ConcurrentHashMap<String, OAuth2Authentication>();

  private final ConcurrentHashMap<String, OAuth2AccessToken> authenticationToAccessTokenStore = new ConcurrentHashMap<String, OAuth2AccessToken>();
  
  public void storeAccessToken(OAuth2AccessToken token, OAuth2Authentication authentication) {
    this.accessTokenStore.put(token.getValue(), token);
    this.authenticationStore.put(token.getValue(), authentication);
    this.authenticationToAccessTokenStore.put(authenticationKeyGenerator.extractKey(authentication), token);
  }
}

# 数据库令牌存储器

/*xxx: jdbc的令牌存储实现,涉及到的表为:oauth_access_token,oauth_refresh_token */
public class JdbcTokenStore implements TokenStore {
  private final JdbcTemplate jdbcTemplate;
}

# redis令牌存储器

/*xxx: 采用redis存储访问令牌*/
public class RedisTokenStore implements TokenStore {
  private final RedisConnectionFactory connectionFactory;
}

# Java Web Token令牌存储器

/*xxx: 采用jwt存储的 令牌存储器 */
public class JwtTokenStore implements TokenStore {
  /*xxx: jwt访问令牌转换器*/
  private JwtAccessTokenConverter jwtTokenEnhancer;
    
  @Override
  /*xxx: jwtTokenStore 不需要存储 令牌与认证信息*/
  /*xxx: 因为它是客户端存储方案*/
  public void storeAccessToken(OAuth2AccessToken token, OAuth2Authentication authentication) {
  }

  @Override
  public void removeAccessToken(OAuth2AccessToken token) {
      /*xxx: do nothing*/
  }

  @Override
  public OAuth2Authentication readAuthentication(String token) {
    return jwtTokenEnhancer.extractAuthentication(jwtTokenEnhancer.decode(token));
  }
}

# 令牌增强器--逻辑设计

public interface TokenEnhancer {
  /*xxx: 增强令牌*/
  OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication);
}
/*xxx: jwt令牌增强器*/
public class JwtAccessTokenConverter implements TokenEnhancer, AccessTokenConverter, InitializingBean {
  private String verifierKey = new RandomValueStringGenerator().generate();

  private Signer signer = new MacSigner(verifierKey);
  
  private SignatureVerifier verifier;

  public void setKeyPair(KeyPair keyPair) {
    RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
    verifier = new RsaVerifier(publicKey);
  }
  
  /*xxx: 对生成的访问令牌进行增强,使其变为 jwt,并 携带认证信息*/
  public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
    DefaultOAuth2AccessToken result = new DefaultOAuth2AccessToken(accessToken);

    /*xxx: 将 认证信息进行编码,编码成 jwt */
    result.setValue(encode(result, authentication));

    /*xxx: 对刷新令牌也进行jwt编码*/
    DefaultOAuth2RefreshToken token = new DefaultOAuth2RefreshToken(
            encode(encodedRefreshToken, authentication));

    result.setRefreshToken(token);

    return result;
  }

  protected String encode(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
   String content = objectMapper.formatMap(tokenConverter.convertAccessToken(accessToken, authentication));
    String token = JwtHelper.encode(content, signer).getEncoded();
    return token;
  }

  protected Map<String, Object> decode(String token) {
    Jwt jwt = JwtHelper.decodeAndVerify(token, verifier);
    String content = jwt.getClaims();
    Map<String, Object> map = objectMapper.parseMap(content);
    return content;
  }
}
/*xxx: 令牌增强链*/
public class TokenEnhancerChain implements TokenEnhancer {
  private List<TokenEnhancer> delegates = Collections.emptyList();

  public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
    OAuth2AccessToken result = accessToken;
    for (TokenEnhancer enhancer : delegates) {
      result = enhancer.enhance(result, authentication);
    }
    return result;
  }
}

# 客户端服务

/*xxx: 客户端服务*/
public interface ClientDetailsService {
  /*xxx: 通过id查询客户端*/
  ClientDetails loadClientByClientId(String clientId) throws ClientRegistrationException;
}
/*xxx: 内存客户端服务*/
public class InMemoryClientDetailsService implements ClientDetailsService {
  private Map<String, ClientDetails> clientDetailsStore = new HashMap<String, ClientDetails>();
}
/*xxx: 数据库客户端服务,涉及到表: oauth_client_details*/
public class JdbcClientDetailsService implements ClientDetailsService, ClientRegistrationService {
  private final JdbcTemplate jdbcTemplate;
}

# oauth请求工厂

public interface OAuth2RequestFactory {
  OAuth2Request createOAuth2Request(AuthorizationRequest request);

  /*xxx: 授权请求*/
  AuthorizationRequest createAuthorizationRequest(Map<String, String> authorizationParameters);

  /*xxx: 令牌请求*/
  TokenRequest createTokenRequest(Map<String, String> requestParameters, ClientDetails authenticatedClient);
}

OAuth2Request与传统的request不同,它更像是一个本地实体,仅能存储信息

# 用户授权处理器

/*xxx: 用户授权处理器*/
public interface UserApprovalHandler {
  /*xxx: 当前oauth是否已授权 */
  boolean isApproved(AuthorizationRequest authorizationRequest,
                     Authentication userAuthentication);

  /*xxx: 查询之前的授权状态 */
  AuthorizationRequest checkForPreApproval(AuthorizationRequest authorizationRequest,
                                           Authentication userAuthentication);

  /*xxx: 更新之后的授权状态*/
  AuthorizationRequest updateAfterApproval(AuthorizationRequest authorizationRequest,
                                           Authentication userAuthentication);

  /*xxx: 获取用户授权状态 */
  Map<String, Object> getUserApprovalRequest(AuthorizationRequest authorizationRequest,
                                             Authentication userAuthentication);
}
/*xxx: 默认的授权状态,存储在 http请求过程中,user_oauth_approval作为参数进行传递*/
public class DefaultUserApprovalHandler implements UserApprovalHandler {
}
/*xxx: 通过tokenStore存储的用户授权管理处理*/
public class TokenStoreUserApprovalHandler implements UserApprovalHandler, InitializingBean {
  private TokenStore tokenStore;
  
  @Override
  public AuthorizationRequest checkForPreApproval(AuthorizationRequest authorizationRequest, Authentication userAuthentication) {
    String clientId = authorizationRequest.getClientId();
    Set<String> scopes = authorizationRequest.getScope();

    ClientDetails client = clientDetailsService.loadClientByClientId(clientId);

    approved = true;
    for (String scope : scopes) {
      if (!client.isAutoApprove(scope)) {
        approved = false;
      }
    }
    if (approved) {
      authorizationRequest.setApproved(true);
      return authorizationRequest;
    }
  }
}
/*xxx: 用户授权存储器的方案实现*/
public class ApprovalStoreUserApprovalHandler implements UserApprovalHandler, InitializingBean {
  private ApprovalStore approvalStore;
  /*xxx: 略*/
}

# 访问令牌转换器

/*xxx: 访问令牌转换器 */
public interface AccessTokenConverter {
  /*xxx: 根据 访问令牌,以及 认证信息,转换为 map对象*/
  Map<String, ?> convertAccessToken(OAuth2AccessToken token, OAuth2Authentication authentication);

  /*xxx: 从map中 提取访问令牌*/
  OAuth2AccessToken extractAccessToken(String value, Map<String, ?> map);

  /*xxx: 从解析的令牌值中,提取认证信息*/
  OAuth2Authentication extractAuthentication(Map<String, ?> map);
}
public class DefaultAccessTokenConverter implements AccessTokenConverter {
  private UserAuthenticationConverter userTokenConverter = new DefaultUserAuthenticationConverter();
}
/*xxx: 用户认证转换器 */
public interface UserAuthenticationConverter {
  Map<String, ?> convertUserAuthentication(Authentication userAuthentication);
}

public class DefaultUserAuthenticationConverter implements UserAuthenticationConverter {
  public Map<String, ?> convertUserAuthentication(Authentication authentication) {
    Map<String, Object> response = new LinkedHashMap<String, Object>();
    response.put(USERNAME, authentication.getName());
    if (authentication.getAuthorities() != null && !authentication.getAuthorities().isEmpty()) {
      response.put(AUTHORITIES, AuthorityUtils.authorityListToSet(authentication.getAuthorities()));
    }
    return response;
  }
}

# oauth2授权端口设计

# 授权流程

@FrameworkEndpoint
@SessionAttributes("authorizationRequest")
public class AuthorizationEndpoint extends AbstractEndpoint {
    
  @RequestMapping(value = "/oauth/authorize")
  /*xxx: 进行授权,获取授权页面*/
  public ModelAndView authorize(Map<String, Object> model, @RequestParam Map<String, String> parameters,
                                SessionStatus sessionStatus, Principal principal) {
    /*xxx: 根据请求参数,创建 oauthRequest实例*/
    AuthorizationRequest authorizationRequest = getOAuth2RequestFactory().createAuthorizationRequest(parameters);

    /*xxx: 对授权模式进行验证,如果是授权码模式,则会返回 code*/
    /*xxx: 获取令牌或者获取授权码,不能同时为空,即springSecurityOauth2需要进行授权的 只有 授权码,以及隐式授权码模式*/
    /*xxx: 密码模式,以及客户端模式,不需要走授权服务器*/
    if (!responseTypes.contains("token") && !responseTypes.contains("code")) {
      throw new UnsupportedResponseTypeException("Unsupported response types: " + responseTypes);
    }

    /*xxx: 如果当前用户未认证, 则抛错*/
    if (!(principal instanceof Authentication) || !((Authentication) principal).isAuthenticated()) {
      throw new InsufficientAuthenticationException(
              "User must be authenticated with Spring Security before authorization can be completed.");
    }

    /*xxx: 通过客户端服务,查询到客户端信息*/
    ClientDetails client = getClientDetailsService().loadClientByClientId(authorizationRequest.getClientId());

    /*xxx: 获取客户端重定向地址*/
    String resolvedRedirect = redirectResolver.resolveRedirect(redirectUriParameter, client);

    authorizationRequest.setRedirectUri(resolvedRedirect);

    /*xxx: 检查之前的授权结果*/
    authorizationRequest = userApprovalHandler.checkForPreApproval(authorizationRequest,
            (Authentication) principal);

    /*xxx: 查看当前请求是否已经被某个用户授权过*/
    boolean approved = userApprovalHandler.isApproved(authorizationRequest, (Authentication) principal);
    authorizationRequest.setApproved(approved);

    /*xxx: 已经授权过,则返回授权结果*/
    if (authorizationRequest.isApproved()) {
      /*xxx: 如果包含 token ,则采用隐式授权码模式*/
      if (responseTypes.contains("token")) {
        /*xxx: 重定向到客户端地址,带有accessToken,token_type,state,expires_in,scope,extra等参数*/
        return getImplicitGrantResponse(authorizationRequest);
      }
      /*xxx: 如果含有code,则采用 授权码模式*/
      if (responseTypes.contains("code")) {
        /*xxx: 重定向到客户端地址,带有 code,state, 同时生成授权码 */
        return new ModelAndView(getAuthorizationCodeResponse(authorizationRequest,
                (Authentication) principal));
      }
    }
    
    /*xxx: 首次进行授权*/
    model.put("authorizationRequest", authorizationRequest);

    return getUserApprovalPageResponse(model, authorizationRequest, (Authentication) principal);
  }

  private ModelAndView getUserApprovalPageResponse(Map<String, Object> model,
                                                   AuthorizationRequest authorizationRequest, Authentication principal) {
    /*xxx: 用户信息,授权信息,客户端信息等*/
    model.putAll(userApprovalHandler.getUserApprovalRequest(authorizationRequest, principal));
    
    /*xxx: 返回用户授权页面*/
    return new ModelAndView("forward:/oauth/confirm_access", model);
  }
}

# 授权结果处理

@FrameworkEndpoint
@SessionAttributes("authorizationRequest")
public class AuthorizationEndpoint extends AbstractEndpoint {
    
  /*xxx: 处理授权结果*/
  @RequestMapping(value = "/oauth/authorize", method = RequestMethod.POST, params = OAuth2Utils.USER_OAUTH_APPROVAL)
  public View approveOrDeny(@RequestParam Map<String, String> approvalParameters, Map<String, ?> model,
                            SessionStatus sessionStatus, Principal principal) {
    /*xxx: 如果当前用户未认证,则进行抛错*/
    if (!(principal instanceof Authentication)) {
    }

    /*xxx: 获取授权请求实例对象*/
    AuthorizationRequest authorizationRequest = (AuthorizationRequest) model.get("authorizationRequest");

    /*xxx: 获取授权类型*/
    Set<String> responseTypes = authorizationRequest.getResponseTypes();

    /*xxx: 更新授权请求的 用户授权状态*/
    authorizationRequest = userApprovalHandler.updateAfterApproval(authorizationRequest,
            (Authentication) principal);
    boolean approved = userApprovalHandler.isApproved(authorizationRequest, (Authentication) principal);
    authorizationRequest.setApproved(approved);

    if (!authorizationRequest.isApproved()) {
        /*xxx: 如果用户授权未通过,则定向到授权失败的ui*/       
    }

    if (responseTypes.contains("token")) {
        /*xxx: 隐式授权码模式,直接返回accessToken,完成授权流程*/
      return getImplicitGrantResponse(authorizationRequest).getView();
    }

    /*xxx: 授权码模式,生成授权码,并将授权码+state,返回给客户端*/
    return getAuthorizationCodeResponse(authorizationRequest, (Authentication) principal);
  }
}

# oauth2获取令牌端口设计

# get方式验证授权码

@FrameworkEndpoint
public class TokenEndpoint extends AbstractEndpoint {
  @RequestMapping(value = "/oauth/token", method=RequestMethod.GET)
  /*xxx: get方式,返回客户端认证令牌*/
  public ResponseEntity<OAuth2AccessToken> getAccessToken(Principal principal, @RequestParam
          Map<String, String> parameters) throws HttpRequestMethodNotSupportedException {
      /*xxx: 是否允许get请求,不允许则会直接抛错*/
    return postAccessToken(principal, parameters);
  }
}

# post方式验证授权码

@FrameworkEndpoint
public class TokenEndpoint extends AbstractEndpoint {
  @RequestMapping(value = "/oauth/token", method=RequestMethod.POST)
  /*xxx: post方式,返回客户端认证令牌*/
  public ResponseEntity<OAuth2AccessToken> postAccessToken(Principal principal, @RequestParam
          Map<String, String> parameters) throws HttpRequestMethodNotSupportedException {
    /*xxx: 获取到客户端信息*/
    ClientDetails authenticatedClient = getClientDetailsService().loadClientByClientId(clientId);

    /*xxx: 创建token 请求*/
    TokenRequest tokenRequest = getOAuth2RequestFactory().createTokenRequest(parameters, authenticatedClient);

    /*xxx: 验证 scope*/
    oAuth2RequestValidator.validateScope(tokenRequest, authenticatedClient);

    /*xxx: 隐式授权码模式,在此处不支持*/
    if (tokenRequest.getGrantType().equals("implicit")) {
      throw new InvalidGrantException("Implicit grant type not supported from token endpoint");
    }

    /*xxx: 如果是刷新令牌,需要进行相应的设置*/
    if (isRefreshTokenRequest(parameters)) {
      tokenRequest.setScope(OAuth2Utils.parseParameterList(parameters.get(OAuth2Utils.SCOPE)));
    }

    /*xxx: 通过 令牌颁发器,颁发令牌*/
    OAuth2AccessToken token = getTokenGranter().grant(tokenRequest.getGrantType(), tokenRequest);

    /*xxx: 禁用缓存*/
    return getResponse(token);
  }
}

# oauth2验证令牌端口设计

# 验证令牌

/*xxx: 资源服务器,拿着令牌,通过这个接口,来进行验证*/
@FrameworkEndpoint
public class CheckTokenEndpoint {
  @RequestMapping(value = "/oauth/check_token")
  @ResponseBody
  public Map<String, ?> checkToken(@RequestParam("token") String value) {
    /*xxx: 通过资源服务器,获取到资源服务令牌*/
    /*xxx: 资源服务器与  授权服务器的 唯一交叉点就在于此,默认情况下,资源服务器令牌服务,与授权服务器令牌服务用的是同一个*/
    OAuth2AccessToken token = resourceServerTokenServices.readAccessToken(value);

    /*xxx: 通过令牌服务,获取认证信息*/
    OAuth2Authentication authentication = resourceServerTokenServices.loadAuthentication(token.getValue());

    Map<String, ?> response = accessTokenConverter.convertAccessToken(token, authentication);

    return response;
  }
}

# oauth2授权服务器认证配置

# 默认认证配置

@Configuration
@ConditionalOnClass(EnableAuthorizationServer.class) /*xxx: 开启了授权服务器时,才生效*/
@ConditionalOnMissingBean(AuthorizationServerConfigurer.class) /*xxx: 没有自定义 授权服务器配置时,才生效*/
@ConditionalOnBean(AuthorizationServerEndpointsConfiguration.class) /*xxx: 配置了授权服务过滤链时,才生效*/
@EnableConfigurationProperties(AuthorizationServerProperties.class)
@Import(AuthorizationServerTokenServicesConfiguration.class)  /*xxx: 引入授权服务器令牌服务配置*/
public class OAuth2AuthorizationServerConfiguration
		extends AuthorizationServerConfigurerAdapter {
    /*xxx: 配置客户端服务*/
  @Override
  public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
    /*xxx: 默认配置了一个在内存中的 client,并配置其 密钥,授权类型,权限,令牌有效时长,重定向地址等*/
    builder.secret(this.details.getClientSecret())
            .resourceIds(this.details.getResourceIds().toArray(new String[0]))
            .authorizedGrantTypes(
                    this.details.getAuthorizedGrantTypes().toArray(new String[0]))
            .authorities(
                    AuthorityUtils.authorityListToSet(this.details.getAuthorities())
                            .toArray(new String[0]))
            .scopes(this.details.getScope().toArray(new String[0]));
  }

  @Override
  /*xxx: 配置授权服务端口地址信息*/
  public void configure(AuthorizationServerEndpointsConfigurer endpoints)
          throws Exception {
    if (this.tokenConverter != null) {
      endpoints.accessTokenConverter(this.tokenConverter);
    }
    if (this.tokenStore != null) {
      endpoints.tokenStore(this.tokenStore);
    }
    if (this.details.getAuthorizedGrantTypes().contains("password")) {
      endpoints.authenticationManager(this.authenticationManager);
    }
  }

  @Override
  /*xxx: 配置授权服务器 认证配置*/
  public void configure(AuthorizationServerSecurityConfigurer security)
          throws Exception {
    security.passwordEncoder(NoOpPasswordEncoder.getInstance());
    if (this.properties.getCheckTokenAccess() != null) {
      security.checkTokenAccess(this.properties.getCheckTokenAccess());
    }
    if (this.properties.getTokenKeyAccess() != null) {
      security.tokenKeyAccess(this.properties.getTokenKeyAccess());
    }
    if (this.properties.getRealm() != null) {
      security.realm(this.properties.getRealm());
    }
  }
  
  @Configuration
  @ConditionalOnMissingBean(BaseClientDetails.class)
  protected static class BaseClientDetailsConfiguration {

    private final OAuth2ClientProperties client;

    protected BaseClientDetailsConfiguration(OAuth2ClientProperties client) {
      this.client = client;
    }

    @Bean
    @ConfigurationProperties(prefix = "security.oauth2.client")
    public BaseClientDetails oauth2ClientDetails() {
      BaseClientDetails details = new BaseClientDetails();
      if (this.client.getClientId() == null) {
        this.client.setClientId(UUID.randomUUID().toString());
      }
      details.setClientId(this.client.getClientId());
      details.setClientSecret(this.client.getClientSecret());
      details.setAuthorizedGrantTypes(Arrays.asList("authorization_code",
              "password", "client_credentials", "implicit", "refresh_token"));
      details.setAuthorities(
              AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_USER"));
      details.setRegisteredRedirectUri(Collections.<String>emptySet());
      return details;
    }

  }
}

# 资源服务器抽象设计

# 资源服务器令牌服务

/*xxx: 资源服务器 令牌服务*/
public interface ResourceServerTokenServices {
  /*xxx: 通过指定的 令牌值,获取认证信息*/
  OAuth2Authentication loadAuthentication(String accessToken) throws AuthenticationException, InvalidTokenException;

  /*xxx: 通过访问令牌的值,获取访问令牌实例*/
  OAuth2AccessToken readAccessToken(String accessToken);
}
/*xxx: 默认的资源服务器令牌服务*/
public class DefaultTokenServices implements AuthorizationServerTokenServices, ResourceServerTokenServices,
		ConsumerTokenServices, InitializingBean {

  /*xxx: 令牌存储器*/
  private TokenStore tokenStore;
    
  /*xxx: 根据令牌,获取认证信息*/
  public OAuth2Authentication loadAuthentication(String accessTokenValue) throws AuthenticationException,
          InvalidTokenException {
    /*xxx: 从令牌存储器中,获取 访问令牌实例 */
    OAuth2AccessToken accessToken = tokenStore.readAccessToken(accessTokenValue);

    /*xxx: 从令牌存储器中,根据访问令牌实例,获取认证信息*/
    OAuth2Authentication result = tokenStore.readAuthentication(accessToken);

    /*xxx: 验证客户端是否合法,如三方取消绑定等*/
    if (clientDetailsService != null) {
      String clientId = result.getOAuth2Request().getClientId();
      try {
        clientDetailsService.loadClientByClientId(clientId);
      }
      catch (ClientRegistrationException e) {
        throw new InvalidTokenException("Client not valid: " + clientId, e);
      }
    }
    return result;
  }

  public OAuth2AccessToken readAccessToken(String accessToken) {
    /*xxx: 通过令牌存储器,获取令牌实例*/
    return tokenStore.readAccessToken(accessToken);
  }
}
/*xxx: 远程令牌服务 */
public class RemoteTokenServices implements ResourceServerTokenServices {
  private RestOperations restTemplate;

  /*xxx: 授权服务器-验证令牌的地址*/
  private String checkTokenEndpointUrl;

  @Override
  /*xxx: 通过 /oauth/token-check接口,获取认证信息*/
  public OAuth2Authentication loadAuthentication(String accessToken) throws AuthenticationException, InvalidTokenException {
    MultiValueMap<String, String> formData = new LinkedMultiValueMap<String, String>();
    formData.add(tokenName, accessToken);
    HttpHeaders headers = new HttpHeaders();
    /*xxx: 通过basic头部认证,去授权服务器验证token*/
    /*xxx: 这个具体是否需要授权,以及何种授权,均由授权服务器进行配置*/
    headers.set("Authorization", getAuthorizationHeader(clientId, clientSecret));
    Map<String, Object> map = postForMap(checkTokenEndpointUrl, formData, headers);

    return tokenConverter.extractAuthentication(map);
  }

  @Override
  /*xxx: 根据token值,获取token实例,不提供支持*/
  public OAuth2AccessToken readAccessToken(String accessToken) {
    throw new UnsupportedOperationException("Not supported: read access token");
  }
}
/*xxx: 通过用户信息验证token */
public class UserInfoTokenServices implements ResourceServerTokenServices {
  /*xxx: 用户信息端口*/
  private final String userInfoEndpointUrl;

  private OAuth2RestOperations restTemplate;

  @Override
  /*xxx: 通过 user-info 接口,获取认证信息*/
  public OAuth2Authentication loadAuthentication(String accessToken)
          throws AuthenticationException, InvalidTokenException {
    Map<String, Object> map = getMap(this.userInfoEndpointUrl, accessToken);
    return extractAuthentication(map);
  }

  /*xxx: 实际上是获取远程用户信息,然后在本地组装令牌*/
  private OAuth2Authentication extractAuthentication(Map<String, Object> map) {
    Object principal = getPrincipal(map);
    List<GrantedAuthority> authorities = this.authoritiesExtractor
            .extractAuthorities(map);
    OAuth2Request request = new OAuth2Request(null, this.clientId, null, true, null,
            null, null, null, null);
    UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(
            principal, "N/A", authorities);
    token.setDetails(map);
    /*xxx: 根据oauth2和authorities组装oauth2令牌*/
    return new OAuth2Authentication(request, token);
  }

  @Override
  /*xxx: 不支持该操作*/
  public OAuth2AccessToken readAccessToken(String accessToken) {
    throw new UnsupportedOperationException("Not supported: read access token");
  }
}
/*xxx: springSocial提供的资源服务器令牌服务*/
public class SpringSocialTokenServices implements ResourceServerTokenServices {
  @Override
  /*xxx: 通过social请求,获取认证信息*/
  public OAuth2Authentication loadAuthentication(String accessToken)
          throws AuthenticationException, InvalidTokenException {
    AccessGrant accessGrant = new AccessGrant(accessToken);
    Connection<?> connection = this.connectionFactory.createConnection(accessGrant);
    /*xxx: 可用于获取远程用户信息*/
    /*xxx: 最终通过一个叫 ApiAdapter的接口,完成请求,并将用户信息封装起来*/
    UserProfile user = connection.fetchUserProfile();
    return extractAuthentication(user);
  }

  /*xxx: 本地组装 oauth2认证*/
  private OAuth2Authentication extractAuthentication(UserProfile user) {
    String principal = user.getUsername();
    List<GrantedAuthority> authorities = AuthorityUtils
            .commaSeparatedStringToAuthorityList("ROLE_USER");
    OAuth2Request request = new OAuth2Request(null, this.clientId, null, true, null,
            null, null, null, null);
    return new OAuth2Authentication(request,
            new UsernamePasswordAuthenticationToken(principal, "N/A", authorities));
  }

  /*xxx: 不支持*/
  @Override
  public OAuth2AccessToken readAccessToken(String accessToken) {
    throw new UnsupportedOperationException("Not supported: read access token");
  }
}

# 令牌存储器

同授权服务器-令牌存储器,略
注意jwt令牌存储器在此处的作用

# 授权服务器端点配置服务

略,见授权服务器

# 资源服务器资源拒绝策略

/*xxx: 内部处理 accessDenied的策略*/
public interface AccessDeniedHandler {
  void handle(HttpServletRequest request, HttpServletResponse response,
              AccessDeniedException accessDeniedException) throws IOException,
          ServletException;
}
public class OAuth2AccessDeniedHandler extends AbstractOAuth2SecurityExceptionHandler implements AccessDeniedHandler {

	public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException authException)
			throws IOException, ServletException {
		doHandle(request, response, authException);
	}

}
public abstract class AbstractOAuth2SecurityExceptionHandler {
  protected final void doHandle(HttpServletRequest request, HttpServletResponse response, Exception authException)
          throws IOException, ServletException {
    result = enhanceResponse(result, authException);
    exceptionRenderer.handleHttpEntityResponse(result, new ServletWebRequest(request, response));
  }

  /*xxx: 子类有用于维持basic登录信息*/
  protected ResponseEntity<OAuth2Exception> enhanceResponse(ResponseEntity<OAuth2Exception> result,
                                                            Exception authException) {
    return result;
  }
}
public class OAuth2AuthenticationEntryPoint extends AbstractOAuth2SecurityExceptionHandler implements
		AuthenticationEntryPoint {
  @Override
  protected ResponseEntity<OAuth2Exception> enhanceResponse(ResponseEntity<OAuth2Exception> response, Exception exception) {
    if (headers.containsKey("WWW-Authenticate")) {
      existing = extractTypePrefix(headers.getFirst("WWW-Authenticate"));
    }

    HttpHeaders update = new HttpHeaders();
    update.putAll(response.getHeaders());
    update.set("WWW-Authenticate", builder.toString());
    return new ResponseEntity<OAuth2Exception>(response.getBody(), update, response.getStatusCode());
  }
}

# oauth2资源服务器流程设计

# 资源服务器集成流程设计

@Import(ResourceServerConfiguration.class)
public @interface EnableResourceServer {
}
@Configuration
public class ResourceServerConfiguration extends WebSecurityConfigurerAdapter implements Ordered {
  /*xxx: 默认的过滤链优先级*/
  private int order = 3;
    
  @Autowired(required = false)
  /*xxx: 令牌存储器*/
  private TokenStore tokenStore;

  @Autowired(required = false)
  /*xxx: 资源服务器令牌服务*/
  private Map<String, ResourceServerTokenServices> tokenServices;

  /*xxx: 资源服务器配置器*/
  private List<ResourceServerConfigurer> configurers = Collections.emptyList();

  @Autowired(required = false)
  /*xxx: 授权服务器端点配置服务*/
  private AuthorizationServerEndpointsConfiguration endpoints;

  @Override
  protected void configure(HttpSecurity http) throws Exception {
    ResourceServerSecurityConfigurer resources = new ResourceServerSecurityConfigurer();

    /*xxx: 从多个资源服务器令牌服务中选择一个合适的*/
    ResourceServerTokenServices services = resolveTokenServices();
    /*xxx: 资源服务器令牌服务,与 令牌存储器 二者选其一*/
    /*xxx: 资源服务器令牌服务  优于 令牌存储器*/
    if (services != null) {
      resources.tokenServices(services);
    }
    else {
      if (tokenStore != null) {
        resources.tokenStore(tokenStore);
      }
      else if (endpoints != null) {
        resources.tokenStore(endpoints.getEndpointsConfigurer().getTokenStore());
      }
    }

    /*xxx: 资源服务器配置器进行配置*/
    for (ResourceServerConfigurer configurer : configurers) {
      configurer.configure(resources);
    }

    /*xxx: 匿名令牌认证器,匿名令牌,也算认证通过*/
    http.authenticationProvider(new AnonymousAuthenticationProvider("default"))
            .exceptionHandling()
            /*xxx: 配置资源拒绝策略*/
            .accessDeniedHandler(resources.getAccessDeniedHandler()).and()
            .sessionManagement()
            .sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
            .csrf().disable();

    /*xxx: 配置收集*/
    http.apply(resources);

    /*xxx: 配置了资源服务器端口*/
    /*xxx: 则当前的过滤器链,仅对oauth2请求有效*/
    if (endpoints != null) {
      /*xxx: 资源服务器过滤链生效规则*/
      http.requestMatcher(new NotOAuthRequestMatcher(endpoints.oauth2EndpointHandlerMapping()));
    }

    /*xxx: 资源服务器配置器对过滤链进行配置*/
    for (ResourceServerConfigurer configurer : configurers) {
        /*xxx: 实际上是配置了受保护的资源*/
      configurer.configure(http);
    }

    /*xxx: 如果没有资源服务器配置器,则所有的资源都需要进行保护*/
    if (configurers.isEmpty()) {
      http.authorizeRequests().anyRequest().authenticated();
    }
  }
}

# 资源服务器认证配置器

/*xxx: 资源服务器配置*/
public final class ResourceServerSecurityConfigurer extends
        SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {
    
  /*xxx: oauth2认证端口*/
  private AuthenticationEntryPoint authenticationEntryPoint = new OAuth2AuthenticationEntryPoint();

  /*xxx: oauth2认证处理过滤器*/
  private OAuth2AuthenticationProcessingFilter resourcesServerFilter;

  @Override
  public void init(HttpSecurity http) throws Exception {
    registerDefaultAuthenticationEntryPoint(http);
  }

  private void registerDefaultAuthenticationEntryPoint(HttpSecurity http) {
    ExceptionHandlingConfigurer<HttpSecurity> exceptionHandling = http
            .getConfigurer(ExceptionHandlingConfigurer.class);
      /*xxx: 省略其它抽象...*/
    exceptionHandling.defaultAuthenticationEntryPointFor(postProcess(authenticationEntryPoint), preferredMatcher);
  }

  @Override
  public void configure(HttpSecurity http) throws Exception {
    /*xxx: 新增oauth认证管理器*/
    AuthenticationManager oauthAuthenticationManager = oauthAuthenticationManager(http);
    /*xxx: 设置oauth2认证过滤器*/
    resourcesServerFilter = new OAuth2AuthenticationProcessingFilter();
    /*xxx: 设置认证端口*/
    resourcesServerFilter.setAuthenticationEntryPoint(authenticationEntryPoint);
    /*xxx: 设置认证管理器*/
    resourcesServerFilter.setAuthenticationManager(oauthAuthenticationManager);
    /*xxx: 省略其它抽象...*/

    http
            .authorizeRequests().expressionHandler(expressionHandler)
            .and()
            /*xxx: 资源服务器过滤器,就是 oauth2认证过滤器*/
            .addFilterBefore(resourcesServerFilter, AbstractPreAuthenticatedProcessingFilter.class)
            .exceptionHandling()
            .accessDeniedHandler(accessDeniedHandler)
            .authenticationEntryPoint(authenticationEntryPoint);
  }
}

# oauth2单点登录

# 单点登录集成设计

@EnableOAuth2Client
@EnableConfigurationProperties(OAuth2SsoProperties.class)
@Import({ OAuth2SsoDefaultConfiguration.class, OAuth2SsoCustomConfiguration.class,
		ResourceServerTokenServicesConfiguration.class })
public @interface EnableOAuth2Sso {
}

# oauth2单点登录--默认配置器

# 默认配置器设计

@Configuration
@Conditional(NeedsWebSecurityCondition.class)/*xxx: 存在@EnableOauth2Sso注解*/
/*xxx: oauth2单点登录的默认配置,生成一条过滤链*/
public class OAuth2SsoDefaultConfiguration extends WebSecurityConfigurerAdapter {
  @Override
  /*xxx: 所有的请求,都需要进行认证*/
  protected void configure(HttpSecurity http) throws Exception {
    http.antMatcher("/**").authorizeRequests().anyRequest().authenticated();
    new SsoSecurityConfigurer(this.applicationContext).configure(http);
  }
}
/*xxx: 单点登录配置器 */
class SsoSecurityConfigurer {
  public void configure(HttpSecurity http) throws Exception {
      /*xxx: 获取sso配置属性 */
    OAuth2SsoProperties sso = this.applicationContext
            .getBean(OAuth2SsoProperties.class);
    /*xxx: 为过滤链添加 认证过滤器*/
    http.apply(new OAuth2ClientAuthenticationConfigurer(oauth2SsoFilter(sso)));
    addAuthenticationEntryPoint(http, sso);
  }

  /*xxx: 添加 OAuth2ClientAuthenticationProcessingFilter,oauth客户端认证过滤器*/
  private OAuth2ClientAuthenticationProcessingFilter oauth2SsoFilter(
          OAuth2SsoProperties sso) {
    /*xxx: 从容器中,获取  userInfoRestTemplateFacotry*/
    OAuth2RestOperations restTemplate = this.applicationContext
            .getBean(UserInfoRestTemplateFactory.class).getUserInfoRestTemplate();
    /*xxx: 从容器中,获取  userInfoRestTemplateFacotry*/
    OAuth2RestOperations restTemplate = this.applicationContext
            .getBean(UserInfoRestTemplateFactory.class).getUserInfoRestTemplate();
    /*xxx: 从容器中获取 资源服务器令牌服务*/
    ResourceServerTokenServices tokenServices = this.applicationContext
            .getBean(ResourceServerTokenServices.class);
    /*xxx: 实例化 oauth2客户端认证过滤器*/
    OAuth2ClientAuthenticationProcessingFilter filter = new OAuth2ClientAuthenticationProcessingFilter(
            sso.getLoginPath());
    filter.setRestTemplate(restTemplate);
    /*xxx: 设置资源服务器令牌服务*/
    filter.setTokenServices(tokenServices);
    return filter;
  }

  /*xxx: 配置认证端口*/
  private void addAuthenticationEntryPoint(HttpSecurity http, OAuth2SsoProperties sso)
          throws Exception {
    ExceptionHandlingConfigurer<HttpSecurity> exceptions = http.exceptionHandling();

    /*xxx: 重定向到登录地址 */
    exceptions.defaultAuthenticationEntryPointFor(
            new LoginUrlAuthenticationEntryPoint(sso.getLoginPath()),
            preferredMatcher);

    /*xxx: 返回401状态码*/
    exceptions.defaultAuthenticationEntryPointFor(
            new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED),
            new RequestHeaderRequestMatcher("X-Requested-With", "XMLHttpRequest"));
  }
}

private static class OAuth2ClientAuthenticationConfigurer
        extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {

  private OAuth2ClientAuthenticationProcessingFilter filter;

  @Override
  public void configure(HttpSecurity builder) throws Exception {
    OAuth2ClientAuthenticationProcessingFilter ssoFilter = this.filter;
    /*xxx: 配置过滤器的  session认证策略*/
    ssoFilter.setSessionAuthenticationStrategy(
            builder.getSharedObject(SessionAuthenticationStrategy.class));
    /*xxx: 将过滤器添加进入过滤链中*/
    builder.addFilterAfter(ssoFilter,
            AbstractPreAuthenticatedProcessingFilter.class);
  }

}

# oauth2客户端认证过滤器设计

/*xxx: oauth2客户端认证处理器*/
public class OAuth2ClientAuthenticationProcessingFilter extends AbstractAuthenticationProcessingFilter {
    /*xxx: oauth2认证数据源*/
	private AuthenticationDetailsSource<HttpServletRequest, ?> authenticationDetailsSource = new OAuth2AuthenticationDetailsSource();

  public OAuth2ClientAuthenticationProcessingFilter(String defaultFilterProcessesUrl) {
    /*xxx: 单点登录的地址  由 sso进行配置,可以与 登录地址不一致 */
    super(defaultFilterProcessesUrl);
    /*xxx: 空认证处理器 */
    setAuthenticationManager(new NoopAuthenticationManager());
    /*xxx: oauth2认证数据源*/
    setAuthenticationDetailsSource(authenticationDetailsSource);
  }

  @Override
  /*xxx: 尝试进行认证*/
  public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
          throws AuthenticationException, IOException, ServletException {

    OAuth2AccessToken accessToken;
    /*xxx: 从 restTemplate中,获取访问令牌 */
    /*xxx: 自环: 第一环:获取授权码; 第二环: 获取 访问码*/
    accessToken = restTemplate.getAccessToken();

    try {
      /*xxx: 从 restTemplate中,获取访问令牌 */
      /*xxx: 自环: 第一环:获取授权码; 第二环: 获取 访问码*/
      accessToken = restTemplate.getAccessToken();
    } catch (OAuth2Exception e) {
      /*xxx: 获取不到则发布  oauth2认证失败事件,并抛出异常,终止后续流程*/
      BadCredentialsException bad = new BadCredentialsException("Could not obtain access token", e);
      publish(new OAuth2AuthenticationFailureEvent(bad));
      throw bad;
    }

    try {
      /*xxx: 通过 资源服务器令牌服务,获取 服务端认证信息*/
      OAuth2Authentication result = tokenServices.loadAuthentication(accessToken.getValue());
      /*xxx: 将认证信息设置到 request中 略*/
      /*xxx: 发布 oauth2认证成功事件*/
      publish(new AuthenticationSuccessEvent(result));
      return result;
    }catch (InvalidTokenException e) {
        /*xxx: 发布oauth2认证失败事件,并抛出异常*/
      BadCredentialsException bad = new BadCredentialsException("Could not obtain user details from token", e);
      publish(new OAuth2AuthenticationFailureEvent(bad));
      throw bad;
    }
  }
}
public interface OAuth2RestOperations extends RestOperations {
  /*xxx: 获取令牌*/
  OAuth2AccessToken getAccessToken() throws UserRedirectRequiredException;
}

public class OAuth2RestTemplate extends RestTemplate implements OAuth2RestOperations {

  /*xxx: oauth2客户端上下文*/
  private OAuth2ClientContext context;
    
  public OAuth2AccessToken getAccessToken() throws UserRedirectRequiredException {
    /*xxx: 从上下文中,获取客户端令牌*/
    OAuth2AccessToken accessToken = context.getAccessToken();

    /*xxx: 如果上下文中,accessToken非法,或不存在*/
    if (accessToken == null || accessToken.isExpired()) {
      try {
        /*xxx: 尝试获取accessToken*/
        accessToken = acquireAccessToken(context);
      }catch (UserRedirectRequiredException e) {
        /*xxx: 获取不到accessToken,则抛出异常,并存储 state值*/
        context.setPreservedState(stateKey, stateToPreserve);
        throw e;
      }
    }
  }

  /*xxx: 获取 accessToken */
  protected OAuth2AccessToken acquireAccessToken(OAuth2ClientContext oauth2Context)
          throws UserRedirectRequiredException {
    /*xxx: 从上下文中,获取 accessTokenRequest实例 */
    AccessTokenRequest accessTokenRequest = oauth2Context.getAccessTokenRequest();

    /*xxx: 从请求中,获取state值*/
    String stateKey = accessTokenRequest.getStateKey();

    /*xxx: 如果存在 stateKey,则将其移除掉,相当于消费掉*/
    if (stateKey != null) {
      accessTokenRequest.setPreservedState(oauth2Context.removePreservedState(stateKey));
    }

    /*xxx: 省略其它抽象...*/
    
    /*xxx: 创建客户端认证令牌,此处会抛出异常,交由 Oauth2ClientContextFilter进行处理*/
    OAuth2AccessToken accessToken = null;
    /*xxx: 通过accessToken提供器,获取accessToken*/
    accessToken = accessTokenProvider.obtainAccessToken(resource, accessTokenRequest);

    /*xxx: 将获取到的 accessToken,保存至上下文*/
    oauth2Context.setAccessToken(accessToken);
    return accessToken;
  }
}

# oauth2客户端上下文

/*xxx: oauth2客户端上下文*/
public interface OAuth2ClientContext {
  /*xxx: 获取accessToken*/
  OAuth2AccessToken getAccessToken();

  /*xxx: 设置accessToken*/
  void setAccessToken(OAuth2AccessToken accessToken);

  /*xxx: 保存 state 值*/
  void setPreservedState(String stateKey, Object preservedState);

  /*xxx 移除 state 值*/
  Object removePreservedState(String stateKey);
}

public class DefaultOAuth2ClientContext implements OAuth2ClientContext, Serializable {
  /*xxx: 该参数存储了 state 与 客户端请求的 uri 映射信息*/
  private Map<String, Object> state = new HashMap<String, Object>();

  /*xxx: accessToken实体*/
  private OAuth2AccessToken accessToken;

  /*xxx: accessToken请求实体*/
  private AccessTokenRequest accessTokenRequest;

  public void setPreservedState(String stateKey, Object preservedState) {
    state.put(stateKey, preservedState);
  }
}

# 获取令牌失败的处理

  • 抛出UserRedirectRequiredException,该异常内部,存储了授权服务器的地址
/*xxx: oauth2用户重定向异常*/
public class UserRedirectRequiredException extends RuntimeException {
  /*xxx: 重定向地址*/
  private final String redirectUri;

  /*xxx: 请求参数*/
  private final Map<String, String> requestParams;

  /*xxx: state值*/
  private String stateKey;
}
  • oauth2客户端上下文过滤链OAuth2ClientContextFilter,对该异常进行处理
public class OAuth2ClientContextFilter implements Filter, InitializingBean {
  /*xxx: 当前的的地址*/
  public static final String CURRENT_URI = "currentUri";

  public void doFilter(ServletRequest servletRequest,
                       ServletResponse servletResponse, FilterChain chain)
          throws IOException, ServletException {
    try {
      chain.doFilter(servletRequest, servletResponse);
    } catch (Exception ex) {
      UserRedirectRequiredException redirect = (UserRedirectRequiredException) throwableAnalyzer
              .getFirstThrowableOfType(
                      UserRedirectRequiredException.class, causeChain);
      if (redirect != null) {
        redirectUser(redirect, request, response);
      }
    }
  }

  protected void redirectUser(UserRedirectRequiredException e,
                              HttpServletRequest request, HttpServletResponse response)
          throws IOException {
    /*xxx: 获取重定向地址*/
    String redirectUri = e.getRedirectUri();

    UriComponentsBuilder builder = UriComponentsBuilder
            .fromHttpUrl(redirectUri);
    
    /*xxx: 省略其它抽象...*/

    /*xxx: 构建参数,设置 state*/
    if (e.getStateKey() != null) {
      builder.queryParam("state", e.getStateKey());
    }

    this.redirectStrategy.sendRedirect(request, response, builder.build()
            .encode().toUriString());
  }
}

# 访问令牌提供器

/*xxx: accessToken提供器*/
public interface AccessTokenProvider {
  /*xxx: 获取 accessToken,获取失败,抛出重定向错误*/
  OAuth2AccessToken obtainAccessToken(OAuth2ProtectedResourceDetails details, AccessTokenRequest parameters)
          throws UserRedirectRequiredException, UserApprovalRequiredException, AccessDeniedException;
  
}
public abstract class OAuth2AccessTokenSupport {
  private RestOperations restTemplate;

  /*xxx: 认证处理器*/
  private ClientAuthenticationHandler authenticationHandler = new DefaultClientAuthenticationHandler();

  /*xxx: 请求增强器*/
  private RequestEnhancer tokenRequestEnhancer = new DefaultRequestEnhancer();

  /*xxx: 获取accessToken*/
  protected OAuth2AccessToken retrieveToken(AccessTokenRequest request, OAuth2ProtectedResourceDetails resource,
                                            MultiValueMap<String, String> form, HttpHeaders headers) throws OAuth2AccessDeniedException {
    /*xxx: 认证前置处理,将参数设置好*/
    /*xxx: 设置参数时,根据配置项进行设置,包括:(header)  以及 (form 跟 query) */
    authenticationHandler.authenticateTokenRequest(resource, form, headers);

    /*xxx: 根据接口,对 form 或者 header 自定义*/
    tokenRequestEnhancer.enhance(request, resource, form, headers);

    final AccessTokenRequest copy = request;
    final ResponseExtractor<OAuth2AccessToken> delegate = getResponseExtractor();
    /*xxx: 将响应结果中的cookie信息拷贝至 oauth2Request实体对象中*/
    /*xxx: 从结果对象中提取 accessToken*/
    ResponseExtractor<OAuth2AccessToken> extractor = new ResponseExtractor<OAuth2AccessToken>() {
      @Override
      public OAuth2AccessToken extractData(ClientHttpResponse response) throws IOException {
        if (response.getHeaders().containsKey("Set-Cookie")) {
          copy.setCookie(response.getHeaders().getFirst("Set-Cookie"));
        }
        return delegate.extractData(response);
      }
    };
    /*xxx: 执行获取 accessToken令牌*/
    return getRestTemplate().execute(getAccessTokenUri(resource, form), getHttpMethod(),
            getRequestCallback(resource, form, headers), extractor, form.toSingleValueMap());
  }

  /*xxx: 获取 accessToken的接口地址*/
  protected String getAccessTokenUri(OAuth2ProtectedResourceDetails resource, MultiValueMap<String, String> form) {
    StringBuilder builder = new StringBuilder(accessTokenUri);

    if (getHttpMethod() == HttpMethod.GET) {
      String separator = "?";
      if (accessTokenUri.contains("?")) {
        separator = "&";
      }

      for (String key : form.keySet()) {
        builder.append(separator);
        builder.append(key + "={" + key + "}");
        separator = "&";
      }
    }

    return builder.toString();
  }
}

# oauth2客户端认证处理器

/*xxx: 客户端认证处理器*/
public interface ClientAuthenticationHandler {
  /*xxx: 配置认证参数*/
  void authenticateTokenRequest(OAuth2ProtectedResourceDetails resource, MultiValueMap<String, String> form,
                                HttpHeaders headers);
}
public class DefaultClientAuthenticationHandler implements ClientAuthenticationHandler {
    
  public void authenticateTokenRequest(OAuth2ProtectedResourceDetails resource, MultiValueMap<String, String> form,
                                       HttpHeaders headers) {
    /*xxx: 默认采用头部认证*/
    AuthenticationScheme scheme = AuthenticationScheme.header;

    /*xxx: 配置客户端认证模式*/
    if (resource.getClientAuthenticationScheme() != null) {
      scheme = resource.getClientAuthenticationScheme();
    }

    switch (scheme) {
      case header:
        form.remove("client_secret");
        headers.add(
                "Authorization",
                String.format(
                        "Basic %s",
                        new String(Base64.encode(String.format("%s:%s", resource.getClientId(),
                                clientSecret).getBytes("UTF-8")), "UTF-8")));
        break;
      case form:
      case query:
        form.set("client_id", resource.getClientId());
        if (StringUtils.hasText(clientSecret)) {
          form.set("client_secret", clientSecret);
        }
        break;
      default:
        throw new IllegalStateException(
                "Default authentication handler doesn't know how to handle scheme: " + scheme);
    }
  }
}

# 授权码模式认证提供器

public class AuthorizationCodeAccessTokenProvider extends OAuth2AccessTokenSupport implements AccessTokenProvider {
  
  public OAuth2AccessToken obtainAccessToken(OAuth2ProtectedResourceDetails details, AccessTokenRequest request)
          throws UserRedirectRequiredException, UserApprovalRequiredException, AccessDeniedException,
          OAuth2AccessDeniedException {
    /*xxx: 获取授权码资源信息*/
    AuthorizationCodeResourceDetails resource = (AuthorizationCodeResourceDetails) details;

    /*xxx: oauth2请求中不包含 授权码时*/
    if (request.getAuthorizationCode() == null) {
      /*xxx: 请求中,不包含 stateKey,则抛错*/
      if (request.getStateKey() == null) {
        throw getRedirectForAuthorization(resource, request);
      }
      /*xxx: 请求授权码,并将之保存在 oauth2请求中*/
      /*xxx: 如果没有获取到授权码,则会抛出异常,阻断流程*/
      obtainAuthorizationCode(resource, request);
    }
    /*xxx: 在已经获得授权码的情况下,提取客户端认证令牌*/
    return retrieveToken(request, resource, getParametersForTokenRequest(resource, request),
            getHeadersForTokenRequest(request)); 
  }

  private MultiValueMap<String, String> getParametersForTokenRequest(AuthorizationCodeResourceDetails resource,
                                                                     AccessTokenRequest request) {
    MultiValueMap<String, String> form = new LinkedMultiValueMap<String, String>();
    form.set("grant_type", "authorization_code");
    form.set("code", request.getAuthorizationCode());

    form.set("redirect_uri", redirectUri);
  }

  public String obtainAuthorizationCode(OAuth2ProtectedResourceDetails details, AccessTokenRequest request)
          throws UserRedirectRequiredException, UserApprovalRequiredException, AccessDeniedException,
          OAuth2AccessDeniedException {
    /*xxx: 调用用户授权地址,该接口会报  304,401,403等才算正常*/
    ResponseEntity<Void> response = getRestTemplate().execute(resource.getUserAuthorizationUri(), HttpMethod.POST,
            getRequestCallback(resource, form, headers), extractor, form.toSingleValueMap());

    /*xxx: 获取客户端重定向地址,从重定向地址中,取出 state,以及code值,并保存 */
    URI location = response.getHeaders().getLocation();
    String query = location.getQuery();
    Map<String, String> map = OAuth2Utils.extractMap(query);

    request.setStateKey(map.get("state"));

    tring code = map.get("code");
    /*xxx: 如果没获取到授权码code,则抛出 用户需要重定向异常,中断流程*/
    if (code == null) {
      throw new UserRedirectRequiredException(location.toString(), form.toSingleValueMap());
    }
    request.set("code", code);
    return code;
  }
}

# 授权之后,维持会话信息

todo:

# 隐式授权码模式认证提供器

public class ImplicitAccessTokenProvider extends OAuth2AccessTokenSupport implements AccessTokenProvider {
  public OAuth2AccessToken obtainAccessToken(OAuth2ProtectedResourceDetails details, AccessTokenRequest request)
          throws UserRedirectRequiredException, AccessDeniedException, OAuth2AccessDeniedException {
    ImplicitResourceDetails resource = (ImplicitResourceDetails) details;
      /*xxx: 与授权码模式相比,省略了获取授权码的步骤*/
      OAuth2AccessToken token = retrieveToken(request,
              resource, getParametersForTokenRequest(resource, request), getHeadersForTokenRequest(request));
      if (token==null) {
        /*xxx: 抛出用户重定向异常*/
        throw new UserRedirectRequiredException(resource.getUserAuthorizationUri(), request.toSingleValueMap());
      }
      return token;
  }

  private MultiValueMap<String, String> getParametersForTokenRequest(ImplicitResourceDetails resource,
                                                                     AccessTokenRequest request) {
    MultiValueMap<String, String> form = new LinkedMultiValueMap<String, String>();
    form.set("response_type", "token");
    form.set("client_id", resource.getClientId());

    form.set("scope", builder.toString());

    form.set("redirect_uri", redirectUri);

    return form;
  }
}

# 客户端模式认证提供器

public class ClientCredentialsAccessTokenProvider extends OAuth2AccessTokenSupport implements AccessTokenProvider {
  public OAuth2AccessToken obtainAccessToken(OAuth2ProtectedResourceDetails details, AccessTokenRequest request)
          throws UserRedirectRequiredException, AccessDeniedException, OAuth2AccessDeniedException {
    /*xxx: 客户模式,啥都不需要干,直接去请求就行*/
    ClientCredentialsResourceDetails resource = (ClientCredentialsResourceDetails) details;
    return retrieveToken(request, resource, getParametersForTokenRequest(resource), new HttpHeaders());
  }

  private MultiValueMap<String, String> getParametersForTokenRequest(ClientCredentialsResourceDetails resource) {

    MultiValueMap<String, String> form = new LinkedMultiValueMap<String, String>();
    form.set("grant_type", "client_credentials");

    form.set("scope", builder.toString());
    
    return form;
  }
}

# 密码模式认证提供器

public class ResourceOwnerPasswordAccessTokenProvider extends OAuth2AccessTokenSupport implements AccessTokenProvider {
  public OAuth2AccessToken obtainAccessToken(OAuth2ProtectedResourceDetails details, AccessTokenRequest request)
          throws UserRedirectRequiredException, AccessDeniedException, OAuth2AccessDeniedException {

    ResourceOwnerPasswordResourceDetails resource = (ResourceOwnerPasswordResourceDetails) details;
    return retrieveToken(request, resource, getParametersForTokenRequest(resource, request), new HttpHeaders());
  }

  private MultiValueMap<String, String> getParametersForTokenRequest(ResourceOwnerPasswordResourceDetails resource, AccessTokenRequest request) {

    MultiValueMap<String, String> form = new LinkedMultiValueMap<String, String>();
    form.set("grant_type", "password");

    /*xxx: 带上参数名,密码*/
    form.set("username", resource.getUsername());
    form.set("password", resource.getPassword());
    form.putAll(request);

    form.set("scope", builder.toString());
    
    return form;
  }
}

# oauth2单点登录--资源服务器令牌服务配置

# 用户信息请求工厂

  • 初始化时,会将oauth2客户端上下文注入
  • 初始化时,会将被保护资源详细信息进行注入
@Configuration
@ConditionalOnMissingBean(AuthorizationServerEndpointsConfiguration.class)/*xxx: 当前应用非授权服务器时,才生效*/
/*xxx: 资源服务器令牌服务配置*/
public class ResourceServerTokenServicesConfiguration {

  @Bean
  @ConditionalOnMissingBean
  /*xxx: 用户信息请求工厂,负责获取 token,授权码等流程*/
  /*xxx: 初始化时,会将oauth2客户端上下文注入*/
  /*xxx: 同时注入被保护资源详细信息*/
  public UserInfoRestTemplateFactory userInfoRestTemplateFactory(
          ObjectProvider<List<UserInfoRestTemplateCustomizer>> customizers,
          ObjectProvider<OAuth2ProtectedResourceDetails> details,
          ObjectProvider<OAuth2ClientContext> oauth2ClientContext) {
    return new DefaultUserInfoRestTemplateFactory(customizers, details,
            oauth2ClientContext);
  }
}

# 远程令牌服务配置

@Configuration
@Conditional(RemoteTokenCondition.class)
/*xxx: 远程令牌服务配置*/
/*xxx: 在没有  jwt,以及 jws环境时,才会生效配置*/
protected static class RemoteTokenServicesConfiguration {
    
}

# 通过授权服务器获取令牌认证信息

@Configuration
@Conditional(TokenInfoCondition.class)
/*xxx: 既没有 token-info-uri配置,也没有 user-info-uri配置时,进行激活,算是一种容错处理*/
/*xxx: 或者 配置了 token-info-uri,并且配置了 prefer-token-info 为true时,进行激活 */
protected static class TokenInfoServicesConfiguration { 
  /*xxx: 资源服务器配置*/
  private final ResourceServerProperties resource;

  @Bean
  /*xxx: 通过  /oauth/token-check 接口实现的 令牌服务*/
  public RemoteTokenServices remoteTokenServices() {
    RemoteTokenServices services = new RemoteTokenServices();
    services.setCheckTokenEndpointUrl(this.resource.getTokenInfoUri());
    services.setClientId(this.resource.getClientId());
    services.setClientSecret(this.resource.getClientSecret());
    return services;
  }
}

# 通过springSocial获取令牌认证信息

@Configuration
@ConditionalOnClass(OAuth2ConnectionFactory.class)
@Conditional(NotTokenInfoCondition.class)
/*xxx: 生效条件为,既不满足 token-info条件,又具有 social的关键类*/
/*xxx: 通过 social实现的令牌服务:根据具体的装配情况,里面兼容了对  user-info的实现*/
protected static class SocialTokenServicesConfiguration {
    /*xxx: 略*/
}

# 通过userInfo组装本地访问令牌获取认证信息

@Configuration
@ConditionalOnMissingClass("org.springframework.social.connect.support.OAuth2ConnectionFactory")
@Conditional(NotTokenInfoCondition.class) 
/*xxx: 当既不满足 从授权服务器获取令牌认证信息,又不满足social的条件时,生效*/
/*xxx: 通过 user-info 实现的 令牌服务 */
protected static class UserInfoTokenServicesConfiguration {
  /*xxx: 资源服务器配置信息*/
  private final ResourceServerProperties sso;
    
  @Bean
  @ConditionalOnMissingBean(ResourceServerTokenServices.class)
  public UserInfoTokenServices userInfoTokenServices() {
    UserInfoTokenServices services = new UserInfoTokenServices(
            this.sso.getUserInfoUri(), this.sso.getClientId());
    services.setRestTemplate(this.restTemplate);
    services.setTokenType(this.sso.getTokenType());
    
    return services;
  }
}

# 本地令牌服务配置(基于jwt的三种方案,先简单做了解,后文会对jwt方案进行详细说明)

# jws获取令牌认证信息(jwt的增强实现,对称密钥)

@Configuration
@Conditional(JwkCondition.class)
/*xxx: jwk令牌存储服务配置*/
protected static class JwkTokenStoreConfiguration {
  /*xxx: 资源服务配置*/
  private final ResourceServerProperties resource;

  @Bean
  @ConditionalOnMissingBean(ResourceServerTokenServices.class)
  public DefaultTokenServices jwkTokenServices(TokenStore jwkTokenStore) {
    DefaultTokenServices services = new DefaultTokenServices();
    services.setTokenStore(jwkTokenStore);
    return services;
  }

  @Bean
  @ConditionalOnMissingBean(TokenStore.class)
  /*xxx: 本质上是 jwt令牌存储,经过了一次解密*/
  public TokenStore jwkTokenStore() {
    return new JwkTokenStore(this.resource.getJwk().getKeySetUri());
  }
}

# jwt获取令牌认证信息(通过密钥值进行签名及验签)

@Configuration
@Conditional(JwtTokenCondition.class)
/*xxx: 配置了: jwt.key-value 或者 jwt.key-uri,则激活该配置 */
/*xxx: jwt令牌服务配置*/
protected static class JwtTokenServicesConfiguration {
  /*xxx: 资源服务器配置*/
  private final ResourceServerProperties resource;

  @Bean
  @ConditionalOnMissingBean(ResourceServerTokenServices.class)
  /*xxx: 本地资源服务器令牌服务*/
  public DefaultTokenServices jwtTokenServices(TokenStore jwtTokenStore) {
    DefaultTokenServices services = new DefaultTokenServices();
    services.setTokenStore(jwtTokenStore);
    return services;
  }

  @Bean
  @ConditionalOnMissingBean(TokenStore.class)
  /*xxx: jwt的令牌存储,是默认的实现,可以通过复写 TokenStore 进行覆盖*/
  public TokenStore jwtTokenStore() {
    return new JwtTokenStore(jwtTokenEnhancer());
  }

  @Bean
  public JwtAccessTokenConverter jwtTokenEnhancer() {
    JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
    /*xxx: 可以直接设置 keyValue*/
    String keyValue = this.resource.getJwt().getKeyValue();
    /*xxx: 如果没有设置 keyValue,则从服务端获取*/
    if (!StringUtils.hasText(keyValue)) {
      keyValue = getKeyFromServer();
    }

    converter.setSigningKey(keyValue);

    converter.setVerifierKey(keyValue);

    return converter;
  }

  /*xxx: 从服务端获取 jwt的 key*/
  private String getKeyFromServer() {
    /*xxx: 获取key时,通过客户端认证方式进行认证*/
    HttpHeaders headers = new HttpHeaders();
    String username = this.resource.getClientId();
    String password = this.resource.getClientSecret();
    if (username != null && password != null) {
      byte[] token = Base64.getEncoder()
              .encode((username + ":" + password).getBytes());
      headers.add("Authorization", "Basic " + new String(token));
    }
    /*xxx: 省略其它抽象*/
    String url = this.resource.getJwt().getKeyUri();
    return (String) keyUriRestTemplate
            .exchange(url, HttpMethod.GET, request, Map.class).getBody()
            .get("value");
  }
}

# jwt存储获取令牌认证信息(通过密码本进行签名及验签)

@Configuration
@Conditional(JwtKeyStoreCondition.class)
/*xxx: 生效条件为: 配置了: jwt.key-store*/
protected class JwtKeyStoreConfiguration implements ApplicationContextAware {
    
  @Bean
  @ConditionalOnMissingBean(ResourceServerTokenServices.class)
  public DefaultTokenServices jwtTokenServices(TokenStore jwtTokenStore) {
    DefaultTokenServices services = new DefaultTokenServices();
    services.setTokenStore(jwtTokenStore);
    return services;
  }

  @Bean
  @ConditionalOnMissingBean(TokenStore.class)
  public TokenStore tokenStore() {
    return new JwtTokenStore(accessTokenConverter());
  }

  @Bean
  public JwtAccessTokenConverter accessTokenConverter() {
    JwtAccessTokenConverter converter = new JwtAccessTokenConverter();

    Resource keyStore = this.context.getResource(this.resource.getJwt().getKeyStore());
    char[] keyStorePassword = this.resource.getJwt().getKeyStorePassword().toCharArray();
    KeyStoreKeyFactory keyStoreKeyFactory = new KeyStoreKeyFactory(keyStore, keyStorePassword);

    String keyAlias = this.resource.getJwt().getKeyAlias();
    char[] keyPassword = Optional.ofNullable(
                    this.resource.getJwt().getKeyPassword())
            .map(String::toCharArray).orElse(keyStorePassword);
    converter.setKeyPair(keyStoreKeyFactory.getKeyPair(keyAlias, keyPassword));

    return converter;
  }
}

# oauth2第三方客户端配置

# 逻辑意义

  • 第三方配置客户端,是相对于oauth2流程中的授权服务器,资源服务器而言;
  • 该配置的意义在于,将客户端导向授权服务器获取授权,通常情况下,用户需要在这个阶段进行认证后,方能进行授权;

# 集成配置

@Import(OAuth2ClientConfiguration.class)
public @interface EnableOAuth2Client {
}

# 客户端配置

@Configuration
/*xxx: oauth2客户端配置*/
public class OAuth2ClientConfiguration {
  @Bean
  /*xxx: oauth2客户端上下文过滤器*/
  public OAuth2ClientContextFilter oauth2ClientContextFilter() {
    OAuth2ClientContextFilter filter = new OAuth2ClientContextFilter();
    return filter;
  }

  @Bean
  @Scope(value = "request", proxyMode = ScopedProxyMode.INTERFACES)
  /*xxx: access令牌的请求实例,在请求时创建,请求完成后销毁,记录了当前请求的地址*/
  protected AccessTokenRequest accessTokenRequest(@Value("#{request.parameterMap}")
                                                          Map<String, String[]> parameters, @Value("#{request.getAttribute('currentUri')}")
                                                          String currentUri) {
    DefaultAccessTokenRequest request = new DefaultAccessTokenRequest(parameters);
    request.setCurrentUri(currentUri);
    return request;
  }

  @Configuration
  /*xxx: oauth2客户端上下文配置*/
  protected static class OAuth2ClientContextConfiguration {

    @Resource
    @Qualifier("accessTokenRequest")
    private AccessTokenRequest accessTokenRequest;

    @Bean
    @Scope(value = "session", proxyMode = ScopedProxyMode.INTERFACES)
    /*xxx: oauth2上下文,创建session域的 客户端上下文bean*/
    public OAuth2ClientContext oauth2ClientContext() {
      return new DefaultOAuth2ClientContext(accessTokenRequest);
    }
  }
}

# 客户端上下文过滤器

public class OAuth2ClientContextFilter implements Filter, InitializingBean {

  /*xxx: 当前的的地址*/
  public static final String CURRENT_URI = "currentUri";
    
  public void doFilter(ServletRequest servletRequest,
                       ServletResponse servletResponse, FilterChain chain)
          throws IOException, ServletException {
    HttpServletRequest request = (HttpServletRequest) servletRequest;
    HttpServletResponse response = (HttpServletResponse) servletResponse;
    /*xxx: 为当前的请求,记录当前请求路径信息*/
    request.setAttribute(CURRENT_URI, calculateCurrentUri(request));
    try {
      chain.doFilter(servletRequest, servletResponse);
    } catch (Exception ex) {
      /*xxx: 在后续过滤器中,如果抛出了 UserRedirectRequiredException,则对其进行重定向*/
      serRedirectRequiredException redirect = (UserRedirectRequiredException) throwableAnalyzer
              .getFirstThrowableOfType(
                      UserRedirectRequiredException.class, causeChain);
      redirectUser(redirect, request, response);
    }
  }

  protected void redirectUser(UserRedirectRequiredException e,
                              HttpServletRequest request, HttpServletResponse response)
          throws IOException {
    /*xxx: 获取重定向地址*/
    String redirectUri = e.getRedirectUri();

    /*xxx: 构建参数,设置 state*/
    if (e.getStateKey() != null) {
      builder.queryParam("state", e.getStateKey());
    }

    this.redirectStrategy.sendRedirect(request, response, builder.build()
            .encode().toUriString());
  }
}

# 如何确保OAuth2ClientContextFilter位于springSecurity过滤链之前

@Configuration
@Import(OAuth2RestOperationsConfiguration.class)
public class OAuth2AutoConfiguration {
    /*xxx: 省略其它抽象...*/
}
@Configuration
@ConditionalOnClass(EnableOAuth2Client.class)
public class OAuth2RestOperationsConfiguration {

  @Configuration
  protected static class SessionScopedConfiguration {

    @Bean
    public FilterRegistrationBean<OAuth2ClientContextFilter> oauth2ClientFilterRegistration(
            OAuth2ClientContextFilter filter, SecurityProperties security) {
      FilterRegistrationBean<OAuth2ClientContextFilter> registration = new FilterRegistrationBean<>();
      registration.setFilter(filter);
      /*xxx: 它的位置位于springSecurity过滤链之前*/
      registration.setOrder(security.getFilter().getOrder() - 10);
      return registration;
    }
  }
  /*xxx: 省略其它抽象...*/
}

# jwt令牌存储技术

# 什么是JWT

  • Json Web Token,是为了在网络应用环境间传递声明,而执行的一种基于JSON的开放标准
  • 该token被设计的紧凑且安全,特别适合分布式站点的单点登录场景

# http协议维持会话的方式

  • 基于Session维持会话(传统)
  • 基于token维持会话

# JWT的组成

一个JWT字符串,由三部分组成,分别是头部header,载荷playload,签名signature。 它们由.连接在一起。

# JWT头部

{
  "typ": "JWT",
  "alg": "HS256"
}

头部声明了两部分内容:

  • 类型: jwt,jws,jwk,jwe
  • 加密算法

# JWT载荷

{
  "sub": "1234567890",
  "name": "John Doe",
  "admin": true
}

jwt用来存放有效信息的地方,内容可以分为三类:

  • 标准中注册的声明:
    • iss: jwt签发者
    • sub: jwt面向的用户
    • aud: 接受jwt的一方
    • exp: jwt的过期时间
    • iat: 签发时间
    • jti: jwt的唯一身份标识
  • 公共的声明: 可添加任何信息
  • 私有的声明: 签发者和消费者共同定义的声明

# jwt签名

由三个签证信息组成:

  • base64后的header的签证信息
  • base64后的payload的签证信息
  • secret盐值

通过header中声明的加密方式,加上secret盐,jwt进行签证;
secret盐值是存放在客户端的,jwt的签发也是在客户端。secret就是用来进行jwt的签发和验证。secret就是服务端的私钥,任何场景下都不能泄露出去。一旦被客户端得知secret,客户端就可以自己签发jwt了。

# 基于JWK的SSO实现说明

  • security.oauth2.resource.jwk.keySetUri,该配置项用于获取RSA公钥,对jwt进行验证;
  • 此种jwt头部采用的是非对称加密算法

# JWT基于secret盐的方式

  • 标准的jwt模式,客户端持有了secret盐,也可以从授权服务器中获取;
  • 配置项为security.oauth2.resource.jwt.key-value以及security.oauth2.resource.jwt.key-uri

# JWT基于本地证书的方式

  • 通过RSA的方式对jwt进行签名及验签
  • 配置项为security.oauth2.resource.jwt.key-store