赞
踩
该类是Spring Boot扫描所有jra目录下META-INFO中spring.factories,完成自动加载。
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.security.oauth2.OAuth2AutoConfiguration
当前类功能:通过@Import完成安全配置
1.OAuth2AuthorizationServerConfiguration,该类需要启动注解@EnableAuthorizationServer才能完成自动引入。
2.OAuth2MethodSecurityConfiguration,该类需要Bean存在GlobalMethodSecurityConfiguration才能完成自动引入。
3.OAuth2MethodSecurityConfiguration,该类
@Configuration @ConditionalOnClass({ OAuth2AccessToken.class, WebMvcConfigurer.class }) @Import({ OAuth2AuthorizationServerConfiguration.class, OAuth2MethodSecurityConfiguration.class, OAuth2ResourceServerConfiguration.class, OAuth2RestOperationsConfiguration.class }) @AutoConfigureBefore(WebMvcAutoConfiguration.class) @EnableConfigurationProperties(OAuth2ClientProperties.class) public class OAuth2AutoConfiguration { private final OAuth2ClientProperties credentials; public OAuth2AutoConfiguration(OAuth2ClientProperties credentials) { this.credentials = credentials; } @Bean public ResourceServerProperties resourceServerProperties() { return new ResourceServerProperties(this.credentials.getClientId(), this.credentials.getClientSecret()); } }
开启注解@EnableAuthorizationServer且继承AuthorizationServerConfigurerAdapter,需要实现AuthorizationServerConfigurerAdapter中的方法。如若不知如何编写可参考OAuth2AuthorizationServerConfiguration中的写法。
@EnableAuthorizationServer
会加载AuthorizationServerEndpointsConfiguration和AuthorizationServerSecurityConfiguration
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({AuthorizationServerEndpointsConfiguration.class, AuthorizationServerSecurityConfiguration.class})
public @interface EnableAuthorizationServer {
}
启动当前类的条件:
1.@ConditionalOnClass(EnableAuthorizationServer.class) 需要在代码中开启@EnableAuthorizationServer注解。
2.@ConditionOnMissingBean(AuthorizationServerConfigurer.class),需要Bean中不存在AuthorizationServerConfigurer。而AuthorizationServerConfigurerAdapter是实现了AuthorizationServerConfigurer。所以只要在代码中实现了AuthorizationServerConfigurerAdapter就不会加载当前类。
3.@ConditionalOnBean(AuthorizationServerEndpointsConfiguration.class),需要Bean中存在AuthorizationServerEndpointsConfiguration类,
4.@EnableConfigurationProperties(AuthorizationServerProperties.class) ,加载配置类信息到AuthorizationServerProperties类中。
5.@Import(AuthorizationServerTokenServicesConfiguration.class),引入AuthorizationServerTokenServicesConfiguration类。
@Configuration
@ConditionalOnClass(EnableAuthorizationServer.class)
@ConditionalOnMissingBean(AuthorizationServerConfigurer.class)
@ConditionalOnBean(AuthorizationServerEndpointsConfiguration.class)
@EnableConfigurationProperties(AuthorizationServerProperties.class)
@Import(AuthorizationServerTokenServicesConfiguration.class)
public class OAuth2AuthorizationServerConfiguration
extends AuthorizationServerConfigurerAdapter {
配置客户端详情信息:clientId(客户端id)、clientSecret(客户端秘钥)、resourceIds(资源id)、authorizedGrantTypes(授权类型)、authorities(权限)、scopes(作用域)等信息。
@Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { ClientDetailsServiceBuilder<InMemoryClientDetailsServiceBuilder>.ClientBuilder builder = clients .inMemory().withClient(this.details.getClientId()); 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])); if (this.details.getAutoApproveScopes() != null) { builder.autoApprove( this.details.getAutoApproveScopes().toArray(new String[0])); } if (this.details.getAccessTokenValiditySeconds() != null) { builder.accessTokenValiditySeconds( this.details.getAccessTokenValiditySeconds()); } if (this.details.getRefreshTokenValiditySeconds() != null) { builder.refreshTokenValiditySeconds( this.details.getRefreshTokenValiditySeconds()); } if (this.details.getRegisteredRedirectUri() != null) { builder.redirectUris( this.details.getRegisteredRedirectUri().toArray(new String[0])); } }
配置认证服务器站点配置
配置AccessTokenConverter(访问Token转换器)
配置TokenStore(Token存储)
如果配置了密码模式授权
,则需要配置AuthenticationManager,用于在ResourceOwnerPasswordTokenGranter(密码授权)时候完成用户校验。
@Override
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);
}
}
详情参考OAuth2AuthorizationServerConfiguration中的配置
AuthorizationServerConfigurerAdapter
/**
* 配置认证服务安全, 通过哪些途径到/oauth/token站点.
* /oauth/authorize站点也是需要保护的, 这是一个面向用户的普通站点,在UI方面需要提供相似的安全服务。 具体详见AuthorizationServerSecurityConfigurer类。
*/
void configure(AuthorizationServerSecurityConfigurer security) throws Exception;
/**
* 配置ClientDetailsService。例如声明单个Clients及其属性,请注意除非在AuthorizationServerEndpointsConfigurer中配置AuthenticationManger。否则不会启用密码授予(即使某些客户端允许这样做)。至少声明一个客户端或完全格式的自定义ClientDetailsService,否则服务器将不会启动。*/
void configure(ClientDetailsServiceConfigurer clients) throws Exception;
/**
*授权服务器配置非安全特性,如令牌存储、令牌自定义、用户批准和授权类型。默认情况下,您不需要做任何事情,除非您需要密码授予,在这种情况下,需要提供AuthenticationManager。
*/
void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception;
创建AuthorizationServerEndpointsConfigurer,并注入ClientDetailsService
初始化:配置授权页面/oauth/confirm_access。异常处理器、异常页面、ClientDetailsService、AuthorizationCodeServices(授权码服务器)、OAuth2请求工厂、OAuth2请求校验、用户授权处理器、回调地址解析器、token授权者。
@Bean public AuthorizationEndpoint authorizationEndpoint() throws Exception { AuthorizationEndpoint authorizationEndpoint = new AuthorizationEndpoint(); FrameworkEndpointHandlerMapping mapping = getEndpointsConfigurer().getFrameworkEndpointHandlerMapping(); authorizationEndpoint.setUserApprovalPage(extractPath(mapping, "/oauth/confirm_access")); authorizationEndpoint.setProviderExceptionHandler(exceptionTranslator()); authorizationEndpoint.setErrorPage(extractPath(mapping, "/oauth/error")); authorizationEndpoint.setTokenGranter(tokenGranter()); authorizationEndpoint.setClientDetailsService(clientDetailsService); authorizationEndpoint.setAuthorizationCodeServices(authorizationCodeServices()); authorizationEndpoint.setOAuth2RequestFactory(oauth2RequestFactory()); authorizationEndpoint.setOAuth2RequestValidator(oauth2RequestValidator()); authorizationEndpoint.setUserApprovalHandler(userApprovalHandler()); authorizationEndpoint.setRedirectResolver(redirectResolver()); return authorizationEndpoint; }
在到达Token站点之前,已经通过ClientCredentialsTokenEndpointFilter完成客户端认证。
这里TokenEndpoint初始化。
@Bean
public TokenEndpoint tokenEndpoint() throws Exception {
TokenEndpoint tokenEndpoint = new TokenEndpoint();
tokenEndpoint.setClientDetailsService(clientDetailsService);
tokenEndpoint.setProviderExceptionHandler(exceptionTranslator());
tokenEndpoint.setTokenGranter(tokenGranter());
tokenEndpoint.setOAuth2RequestFactory(oauth2RequestFactory());
tokenEndpoint.setOAuth2RequestValidator(oauth2RequestValidator());
tokenEndpoint.setAllowedRequestMethods(allowedTokenEndpointRequestMethods());
return tokenEndpoint;
}
@Bean
public CheckTokenEndpoint checkTokenEndpoint() {
CheckTokenEndpoint endpoint = new CheckTokenEndpoint(getEndpointsConfigurer().getResourceServerTokenServices());
endpoint.setAccessTokenConverter(getEndpointsConfigurer().getAccessTokenConverter());
endpoint.setExceptionTranslator(exceptionTranslator());
return endpoint;
}
@Bean
public WhitelabelApprovalEndpoint whitelabelApprovalEndpoint() {
return new WhitelabelApprovalEndpoint();
}
@Bean
public WhitelabelErrorEndpoint whitelabelErrorEndpoint() {
return new WhitelabelErrorEndpoint();
}
@Component protected static class TokenKeyEndpointRegistrar implements BeanDefinitionRegistryPostProcessor { private BeanDefinitionRegistry registry; @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { String[] names = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, JwtAccessTokenConverter.class, false, false); if (names.length > 0) { BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(TokenKeyEndpoint.class); builder.addConstructorArgReference(names[0]); registry.registerBeanDefinition(TokenKeyEndpoint.class.getName(), builder.getBeanDefinition()); } } }
这个类可以在AuthorizationServerConfigurerAdapter中进行相应的修改。
当如果未设置AuthorizationServerTokenServices,则采用默认的DefaultTokenService服务,
并设置ClientDetailsService的认证管理器为PreAuthenticatedAuthenticationProvider。来完成客户端认证。
public AuthorizationServerTokenServices getTokenServices() { return ProxyCreator.getProxy(AuthorizationServerTokenServices.class, new ObjectFactory<AuthorizationServerTokenServices>() { @Override public AuthorizationServerTokenServices getObject() throws BeansException { return tokenServices(); } }); } private AuthorizationServerTokenServices tokenServices() { if (tokenServices != null) { return tokenServices; } this.tokenServices = createDefaultTokenServices(); return tokenServices; } private DefaultTokenServices createDefaultTokenServices() { DefaultTokenServices tokenServices = new DefaultTokenServices(); tokenServices.setTokenStore(tokenStore()); tokenServices.setSupportRefreshToken(true); tokenServices.setReuseRefreshToken(reuseRefreshToken); tokenServices.setClientDetailsService(clientDetailsService()); tokenServices.setTokenEnhancer(tokenEnhancer()); addUserDetailsService(tokenServices, this.userDetailsService); return tokenServices; } private void addUserDetailsService(DefaultTokenServices tokenServices, UserDetailsService userDetailsService) { if (userDetailsService != null) { PreAuthenticatedAuthenticationProvider provider = new PreAuthenticatedAuthenticationProvider(); provider.setPreAuthenticatedUserDetailsService(new UserDetailsByNameServiceWrapper<PreAuthenticatedAuthenticationToken>( userDetailsService)); tokenServices .setAuthenticationManager(new ProviderManager(Arrays.<AuthenticationProvider> asList(provider))); } }
当tokenGranter为空,则将CompositeTokenGranter类代理getDefaultTokenGranters方法创建的5种TokenGranter:AuthorizationCodeTokenGranter、RefreshTokenGranter、ImplicitTokenGranter、ClientCredentialsTokenGranter、ResourceOwnerPasswordTokenGranter
public TokenGranter getTokenGranter() { return tokenGranter(); } private TokenGranter tokenGranter() { if (tokenGranter == null) { tokenGranter = new TokenGranter() { private CompositeTokenGranter delegate; @Override public OAuth2AccessToken grant(String grantType, TokenRequest tokenRequest) { if (delegate == null) { delegate = new CompositeTokenGranter(getDefaultTokenGranters()); } return delegate.grant(grantType, tokenRequest); } }; } return tokenGranter; } private List<TokenGranter> getDefaultTokenGranters() { ClientDetailsService clientDetails = clientDetailsService(); AuthorizationServerTokenServices tokenServices = tokenServices(); AuthorizationCodeServices authorizationCodeServices = authorizationCodeServices(); OAuth2RequestFactory requestFactory = requestFactory(); List<TokenGranter> tokenGranters = new ArrayList<TokenGranter>(); tokenGranters.add(new AuthorizationCodeTokenGranter(tokenServices, authorizationCodeServices, clientDetails, requestFactory)); tokenGranters.add(new RefreshTokenGranter(tokenServices, clientDetails, requestFactory)); ImplicitTokenGranter implicit = new ImplicitTokenGranter(tokenServices, clientDetails, requestFactory); tokenGranters.add(implicit); tokenGranters.add(new ClientCredentialsTokenGranter(tokenServices, clientDetails, requestFactory)); if (authenticationManager != null) { tokenGranters.add(new ResourceOwnerPasswordTokenGranter(authenticationManager, tokenServices, clientDetails, requestFactory)); } return tokenGranters; }
默认返回的是DefaultWebResponseExceptionTranslator。
public WebResponseExceptionTranslator<OAuth2Exception> getExceptionTranslator() {
return exceptionTranslator();
}
private WebResponseExceptionTranslator<OAuth2Exception> exceptionTranslator() {
if (exceptionTranslator != null) {
return exceptionTranslator;
}
exceptionTranslator = new DefaultWebResponseExceptionTranslator();
return exceptionTranslator;
}
如果未空则采用默认的ResourceServerTokenServices,由方法createDefaultTokenServices创建详情见AuthorizationServerTokenServices创建。
public ResourceServerTokenServices getResourceServerTokenServices() {
return resourceTokenServices();
}
private ResourceServerTokenServices resourceTokenServices() {
if (resourceTokenServices == null) {
if (tokenServices instanceof ResourceServerTokenServices) {
return (ResourceServerTokenServices) tokenServices;
}
resourceTokenServices = createDefaultTokenServices();
}
return resourceTokenServices;
}
如果未设置TokenStore,则判断AccessTokenConverter(访问Token转换器)是否是JwtAccessTokenConverter。如果是则创建JwtTokenStore(JwtToekn存储),否则则采用InMemoryTokenStore(内存Token存储)
public TokenStore getTokenStore() {
return tokenStore();
}
private TokenStore tokenStore() {
if (tokenStore == null) {
if (accessTokenConverter() instanceof JwtAccessTokenConverter) {
this.tokenStore = new JwtTokenStore((JwtAccessTokenConverter) accessTokenConverter());
}
else {
this.tokenStore = new InMemoryTokenStore();
}
}
return this.tokenStore;
}
如果未设置,则创建DefaultAccessTokenConverter(默认访问转换器)
private AccessTokenConverter accessTokenConverter() {
if (this.accessTokenConverter == null) {
accessTokenConverter = new DefaultAccessTokenConverter();
}
return this.accessTokenConverter;
}
用于在TokenGranter为密码
时候需要使用
public AuthorizationServerEndpointsConfigurer authenticationManager(AuthenticationManager authenticationManager) {
this.authenticationManager = authenticationManager;
return this;
}
主要完成BasicAuthenticationFilter的初始化,主要领域是/oauth/client
。其中主要是check_token时候需要根据Basic 完成客户端认证, 才能请求到/oauth/check_token
方法。
@Override public void init(HttpSecurity http) throws Exception { registerDefaultAuthenticationEntryPoint(http); if (passwordEncoder != null) { ClientDetailsUserDetailsService clientDetailsUserDetailsService = new ClientDetailsUserDetailsService(clientDetailsService()); clientDetailsUserDetailsService.setPasswordEncoder(passwordEncoder()); http.getSharedObject(AuthenticationManagerBuilder.class) .userDetailsService(clientDetailsUserDetailsService) .passwordEncoder(passwordEncoder()); } else { http.userDetailsService(new ClientDetailsUserDetailsService(clientDetailsService())); } http.securityContext().securityContextRepository(new NullSecurityContextRepository()).and().csrf().disable() .httpBasic().realmName(realm); if (sslOnly) { http.requiresChannel().anyRequest().requiresSecure(); } }
创建AuthorizationServerSecurityConfigurer。
1.这里完成站点权限的配置:/oauth/token_key、/oauth/check_token。
2.http中设置ClientDetailsService为clientDetailsService方便,后续使用。
3.
@Override protected void configure(HttpSecurity http) throws Exception { AuthorizationServerSecurityConfigurer configurer = new AuthorizationServerSecurityConfigurer(); FrameworkEndpointHandlerMapping handlerMapping = endpoints.oauth2EndpointHandlerMapping(); http.setSharedObject(FrameworkEndpointHandlerMapping.class, handlerMapping); configure(configurer); http.apply(configurer); String tokenEndpointPath = handlerMapping.getServletPath("/oauth/token"); String tokenKeyPath = handlerMapping.getServletPath("/oauth/token_key"); String checkTokenPath = handlerMapping.getServletPath("/oauth/check_token"); if (!endpoints.getEndpointsConfigurer().isUserDetailsServiceOverride()) { UserDetailsService userDetailsService = http.getSharedObject(UserDetailsService.class); endpoints.getEndpointsConfigurer().userDetailsService(userDetailsService); } // @formatter:off http .authorizeRequests() .antMatchers(tokenEndpointPath).fullyAuthenticated() .antMatchers(tokenKeyPath).access(configurer.getTokenKeyAccess()) .antMatchers(checkTokenPath).access(configurer.getCheckTokenAccess()) .and() .requestMatchers() .antMatchers(tokenEndpointPath, tokenKeyPath, checkTokenPath) .and() .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.NEVER); // @formatter:on http.setSharedObject(ClientDetailsService.class, clientDetailsService); }
该类继承了SecurityConfigurerAdapter,并可在AuthorizationServerConfigurerAdapter类中修改
由clientCredentialsTokenEndpointFilter完成ClientCredentialsTokenEndpointFilter过滤链的创建。
@Override
public void configure(HttpSecurity http) throws Exception {
// ensure this is initialized
frameworkEndpointHandlerMapping();
if (allowFormAuthenticationForClients) {
clientCredentialsTokenEndpointFilter(http);
}
for (Filter filter : tokenEndpointAuthenticationFilters) {
http.addFilterBefore(filter, BasicAuthenticationFilter.class);
}
http.exceptionHandling().accessDeniedHandler(accessDeniedHandler);
}
1.主要是设置UserDetailsService为ClientDetailsUserDetailsService
2.完成BasicAuthenticationFilter的配置,用于在/oauth/token
之外的的接口中验证客户端信息
@Override public void init(HttpSecurity http) throws Exception { registerDefaultAuthenticationEntryPoint(http); if (passwordEncoder != null) { ClientDetailsUserDetailsService clientDetailsUserDetailsService = new ClientDetailsUserDetailsService(clientDetailsService()); clientDetailsUserDetailsService.setPasswordEncoder(passwordEncoder()); http.getSharedObject(AuthenticationManagerBuilder.class) .userDetailsService(clientDetailsUserDetailsService) .passwordEncoder(passwordEncoder()); } else { http.userDetailsService(new ClientDetailsUserDetailsService(clientDetailsService())); } http.securityContext().securityContextRepository(new NullSecurityContextRepository()).and().csrf().disable() .httpBasic().realmName(realm); if (sslOnly) { http.requiresChannel().anyRequest().requiresSecure(); } }
初始化ClientCredentialsTokenEndpointFilter 并设置过滤地址/oauth/token
,
设置AuthenticationManager为客户端信息完成认证
设置UserDetailsService为ClientDetailsUserDetailsService
private ClientCredentialsTokenEndpointFilter clientCredentialsTokenEndpointFilter(HttpSecurity http) {
ClientCredentialsTokenEndpointFilter clientCredentialsTokenEndpointFilter = new ClientCredentialsTokenEndpointFilter(
frameworkEndpointHandlerMapping().getServletPath("/oauth/token"));
clientCredentialsTokenEndpointFilter
.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class));
OAuth2AuthenticationEntryPoint authenticationEntryPoint = new OAuth2AuthenticationEntryPoint();
authenticationEntryPoint.setTypeName("Form");
authenticationEntryPoint.setRealmName(realm);
clientCredentialsTokenEndpointFilter.setAuthenticationEntryPoint(authenticationEntryPoint);
clientCredentialsTokenEndpointFilter = postProcess(clientCredentialsTokenEndpointFilter);
http.addFilterBefore(clientCredentialsTokenEndpointFilter, BasicAuthenticationFilter.class);
return clientCredentialsTokenEndpointFilter;
}
站点的创建主要由AuthorizationServerEndpointsConfiguration创建。
用于第三方授权的端点,
@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); Set<String> responseTypes = authorizationRequest.getResponseTypes(); if (!responseTypes.contains("token") && !responseTypes.contains("code")) { throw new UnsupportedResponseTypeException("Unsupported response types: " + responseTypes); } if (authorizationRequest.getClientId() == null) { throw new InvalidClientException("A client id must be provided"); } 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()); String redirectUriParameter = authorizationRequest.getRequestParameters().get(OAuth2Utils.REDIRECT_URI); String resolvedRedirect = redirectResolver.resolveRedirect(redirectUriParameter, client); if (!StringUtils.hasText(resolvedRedirect)) { throw new RedirectMismatchException( "A redirectUri must be either supplied or preconfigured in the ClientDetails"); } authorizationRequest.setRedirectUri(resolvedRedirect); oauth2RequestValidator.validateScope(authorizationRequest, client); authorizationRequest = userApprovalHandler.checkForPreApproval(authorizationRequest, (Authentication) principal); boolean approved = userApprovalHandler.isApproved(authorizationRequest, (Authentication) principal); authorizationRequest.setApproved(approved); if (authorizationRequest.isApproved()) { if (responseTypes.contains("token")) { return getImplicitGrantResponse(authorizationRequest); } if (responseTypes.contains("code")) { return new ModelAndView(getAuthorizationCodeResponse(authorizationRequest, (Authentication) principal)); } } model.put(AUTHORIZATION_REQUEST_ATTR_NAME, authorizationRequest); model.put(ORIGINAL_AUTHORIZATION_REQUEST_ATTR_NAME, unmodifiableMap(authorizationRequest)); return getUserApprovalPageResponse(model, authorizationRequest, (Authentication) principal); } catch (RuntimeException e) { sessionStatus.setComplete(); throw e; } }
经过ClientCredentialsTokenEndpointFilter过滤器完成客户端认证,在进入到当前/oauth/token
接口。
1.判断是否已通过客户端认证。未认证则直接抛出异常。
2.获取clientId,在根据clientId从ClientDetaisService中获取ClientDeatias。
3.请求参数和ClientDeatias通过OAuth2RequestFactory转换为当前方法需要的TokenRequest。
4.如果ClientDetails不为空通过OAuth2RequestValidator完成请求验证,确保输入参数正确。
5.当前类不支持implicit模式授权
6.在通过TokenGranter中的代理完成,授权类型匹配,进行相应的授权。
@RequestMapping(value = "/oauth/token", method=RequestMethod.POST) public ResponseEntity<OAuth2AccessToken> postAccessToken(Principal principal, @RequestParam Map<String, String> parameters) throws HttpRequestMethodNotSupportedException { if (!(principal instanceof Authentication)) { throw new InsufficientAuthenticationException( "There is no client authentication. Try adding an appropriate authentication filter."); } String clientId = getClientId(principal); ClientDetails authenticatedClient = getClientDetailsService().loadClientByClientId(clientId); TokenRequest tokenRequest = getOAuth2RequestFactory().createTokenRequest(parameters, authenticatedClient); if (clientId != null && !clientId.equals("")) { // Only validate the client details if a client authenticated during this // request. if (!clientId.equals(tokenRequest.getClientId())) { // double check to make sure that the client ID in the token request is the same as that in the // authenticated client throw new InvalidClientException("Given client ID does not match authenticated client"); } } if (authenticatedClient != null) { oAuth2RequestValidator.validateScope(tokenRequest, authenticatedClient); } if (!StringUtils.hasText(tokenRequest.getGrantType())) { throw new InvalidRequestException("Missing grant type"); } if (tokenRequest.getGrantType().equals("implicit")) { throw new InvalidGrantException("Implicit grant type not supported from token endpoint"); } if (isAuthCodeRequest(parameters)) { // The scope was requested or determined during the authorization step if (!tokenRequest.getScope().isEmpty()) { logger.debug("Clearing scope of incoming token request"); tokenRequest.setScope(Collections.<String> emptySet()); } } if (isRefreshTokenRequest(parameters)) { // A refresh token has its own default scopes, so we should ignore any added by the factory here. tokenRequest.setScope(OAuth2Utils.parseParameterList(parameters.get(OAuth2Utils.SCOPE))); } OAuth2AccessToken token = getTokenGranter().grant(tokenRequest.getGrantType(), tokenRequest); if (token == null) { throw new UnsupportedGrantTypeException("Unsupported grant type: " + tokenRequest.getGrantType()); } return getResponse(token); }
获取到加密的方式,默认是请求无权限的,需要再AuthorizationServerConfigurerAdapter中配置为允许所有请求访问
。
通过JwtAccessTokenConverter返回需要的加密信息。
@RequestMapping(value = "/oauth/token_key", method = RequestMethod.GET)
@ResponseBody
public Map<String, String> getKey(Principal principal) {
if ((principal == null || principal instanceof AnonymousAuthenticationToken) && !converter.isPublic()) {
throw new AccessDeniedException("You need to authenticate to see a shared key");
}
Map<String, String> result = converter.getKey();
return result;
}
在授权码类型授权时候,用户权限授权的页面。
@RequestMapping("/oauth/confirm_access") public ModelAndView getAccessConfirmation(Map<String, Object> model, HttpServletRequest request) throws Exception { final String approvalContent = createTemplate(model, request); if (request.getAttribute("_csrf") != null) { model.put("_csrf", request.getAttribute("_csrf")); } View approvalView = new View() { @Override public String getContentType() { return "text/html"; } @Override public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception { response.setContentType(getContentType()); response.getWriter().append(approvalContent); } }; return new ModelAndView(approvalView, model); }
授权异常页面:在授权码类型中
@RequestMapping("/oauth/error") public ModelAndView handleError(HttpServletRequest request) { Map<String, Object> model = new HashMap<String, Object>(); Object error = request.getAttribute("error"); String errorSummary; if (error instanceof OAuth2Exception) { OAuth2Exception oauthError = (OAuth2Exception) error; errorSummary = HtmlUtils.htmlEscape(oauthError.getSummary()); } else { errorSummary = "Unknown error"; } final String errorContent = ERROR.replace("%errorSummary%", errorSummary); View errorView = new View() { @Override public String getContentType() { return "text/html"; } @Override public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception { response.setContentType(getContentType()); response.getWriter().append(errorContent); } }; return new ModelAndView(errorView, model); }
该类的创建在AuthorizationServerEndpointsConfigurer的tokenGranter方法中。
通过grantType(授权类型)判断当前支持,不支持则循环下一个。
public OAuth2AccessToken grant(String grantType, TokenRequest tokenRequest) {
for (TokenGranter granter : tokenGranters) {
OAuth2AccessToken grant = granter.grant(grantType, tokenRequest);
if (grant!=null) {
return grant;
}
}
return null;
}
grant方法
1.判断grantType是否和当前类的grantType(授权类型)相等。不相等则返回null。
2.根据clientId从ClientDetailsService获取ClientDetails(客户端详情)。
3.从客户端详情中,校验是否支持当前grantType。不支持则抛出异常“Unauthorization grant type ”。
4.通过getAccessToken方法返回OAuth2AccessToken。
public OAuth2AccessToken grant(String grantType, TokenRequest tokenRequest) { if (!this.grantType.equals(grantType)) { return null; } String clientId = tokenRequest.getClientId(); ClientDetails client = clientDetailsService.loadClientByClientId(clientId); validateGrantType(grantType, client); if (logger.isDebugEnabled()) { logger.debug("Getting access token for: " + clientId); } return getAccessToken(client, tokenRequest); }
getAccessToken
根据
protected OAuth2AccessToken getAccessToken(ClientDetails client, TokenRequest tokenRequest) {
return tokenServices.createAccessToken(getOAuth2Authentication(client, tokenRequest));
}
getOAuth2Authentication
根据客户端信息和TokenReques,获取到OAuth2Authentication。
下面很多类都是重写当前方法
protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest tokenRequest) {
OAuth2Request storedOAuth2Request = requestFactory.createOAuth2Request(client, tokenRequest);
return new OAuth2Authentication(storedOAuth2Request, null);
}
支持的授权类型password
private static final String GRANT_TYPE = "password";
通过ClientCredentialsTokenEndpointFilter认证客户端信息,在到/oauth/token
中通过代理CompositeTokenGranter代理,匹配对应授权类型,到达OAuth2Authentication。
1.获取参数中的username 和 password,创建UsernamePasswordAuthenticationToken。
2.通过AuthorizationServerConfigurerAdapter中的配置方AuthorizationServerEndpointsConfigurer配置了AuthenticationManger(认证管理)。完成用户认证。
3.认证完成之后通过OAuth2RequestFactory创建OAuth2Authentication。
@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"); // Protect from downstream leaks of password parameters.remove("password"); Authentication userAuth = new UsernamePasswordAuthenticationToken(username, password); ((AbstractAuthenticationToken) userAuth).setDetails(parameters); try { userAuth = authenticationManager.authenticate(userAuth); } catch (AccountStatusException ase) { //covers expired, locked, disabled cases (mentioned in section 5.2, draft 31) throw new InvalidGrantException(ase.getMessage()); } catch (BadCredentialsException e) { // If the username/password are wrong the spec says we should send 400/invalid grant throw new InvalidGrantException(e.getMessage()); } if (userAuth == null || !userAuth.isAuthenticated()) { throw new InvalidGrantException("Could not authenticate user: " + username); } OAuth2Request storedOAuth2Request = getRequestFactory().createOAuth2Request(client, tokenRequest); return new OAuth2Authentication(storedOAuth2Request, userAuth); }
支持授权类型refresh_token
private static final String GRANT_TYPE = "refresh_token";
从参数refersh_token中获取到值,在从TokenServices中刷新OAuth2AccessToken
@Override
protected OAuth2AccessToken getAccessToken(ClientDetails client, TokenRequest tokenRequest) {
String refreshToken = tokenRequest.getRequestParameters().get("refresh_token");
return getTokenServices().refreshAccessToken(refreshToken, tokenRequest);
}
支持授权类型:authorization_code
private static final String GRANT_TYPE = "authorization_code";
1.获取通过/oatuh/authorize获取的的code,请求回调地址。
2.通过code从AuthorizationCodeServices中获取OAuth2Authentication认证信息。
3.判断回调地址和获取的认证信息中的回调地址是否相等,如果不相等则抛出“Redirect URI mismatch”(回调地址不匹配)
4.将OAuth2Authentication的信息重新组装为OAuth2Authentication
@Override protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest tokenRequest) { Map<String, String> parameters = tokenRequest.getRequestParameters(); String authorizationCode = parameters.get("code"); String redirectUri = parameters.get(OAuth2Utils.REDIRECT_URI); if (authorizationCode == null) { throw new InvalidRequestException("An authorization code must be supplied."); } OAuth2Authentication storedAuth = authorizationCodeServices.consumeAuthorizationCode(authorizationCode); if (storedAuth == null) { throw new InvalidGrantException("Invalid authorization code: " + authorizationCode); } OAuth2Request pendingOAuth2Request = storedAuth.getOAuth2Request(); // https://jira.springsource.org/browse/SECOAUTH-333 // This might be null, if the authorization was done without the redirect_uri parameter String redirectUriApprovalParameter = pendingOAuth2Request.getRequestParameters().get( OAuth2Utils.REDIRECT_URI); if ((redirectUri != null || redirectUriApprovalParameter != null) && !pendingOAuth2Request.getRedirectUri().equals(redirectUri)) { throw new RedirectMismatchException("Redirect URI mismatch."); } String pendingClientId = pendingOAuth2Request.getClientId(); String clientId = tokenRequest.getClientId(); if (clientId != null && !clientId.equals(pendingClientId)) { // just a sanity check. throw new InvalidClientException("Client ID mismatch"); } // Secret is not required in the authorization request, so it won't be available // in the pendingAuthorizationRequest. We do want to check that a secret is provided // in the token request, but that happens elsewhere. Map<String, String> combinedParameters = new HashMap<String, String>(pendingOAuth2Request .getRequestParameters()); // Combine the parameters adding the new ones last so they override if there are any clashes combinedParameters.putAll(parameters); // Make a new stored request with the combined parameters OAuth2Request finalStoredOAuth2Request = pendingOAuth2Request.createOAuth2Request(combinedParameters); Authentication userAuth = storedAuth.getUserAuthentication(); return new OAuth2Authentication(finalStoredOAuth2Request, userAuth); }
支持授权类型:implicit
private static final String GRANT_TYPE = "implicit";
从上下文中中获取Authentication(认证信息)
判断认证信息是否为空,为空则抛出异常
否则创建OAuth2Authentication,并返回
@Override
protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest clientToken) {
Authentication userAuth = SecurityContextHolder.getContext().getAuthentication();
if (userAuth==null || !userAuth.isAuthenticated()) {
throw new InsufficientAuthenticationException("There is no currently logged in user");
}
Assert.state(clientToken instanceof ImplicitTokenRequest, "An ImplicitTokenRequest is required here. Caller needs to wrap the TokenRequest.");
OAuth2Request requestForStorage = ((ImplicitTokenRequest)clientToken).getOAuth2Request();
return new OAuth2Authentication(requestForStorage, userAuth);
}
支持授权码类型:client_credentials
private static final String GRANT_TYPE = "client_credentials";
通过抽象类的grant方法,返回OAuth2AccessToken。
在讲OAuth2AccessToken通过DefaultOAuth2AccessToken创建,并返回。
@Override
public OAuth2AccessToken grant(String grantType, TokenRequest tokenRequest) {
OAuth2AccessToken token = super.grant(grantType, tokenRequest);
if (token != null) {
DefaultOAuth2AccessToken norefresh = new DefaultOAuth2AccessToken(token);
// The spec says that client credentials should not be allowed to get a refresh token
if (!allowRefresh) {
norefresh.setRefreshToken(null);
}
token = norefresh;
}
return token;
}
开启注解@EnableResourceServer且继承ResourceServerConfigurerAdapter,需要实现ResourceServerConfigurerAdapter中的方法。如若不知如何编写可参考ResourceServerConfigurer中的写法。
EnableResourceServer
OAuth2ResourceServerConfiguration
该类由OAuth2AutoConfiguration通过@Import引入
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
}
@Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().anyRequest().authenticated();
}
继承了WebSecurityConfigurerAdapter,实现了资源安全的配置
1.创建ResourceServerSecurityConfigurer
2.创建ResourceServerTokenServices
protected void configure(HttpSecurity http) throws Exception { ResourceServerSecurityConfigurer resources = new ResourceServerSecurityConfigurer(); 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); } // @formatter:off http.authenticationProvider(new AnonymousAuthenticationProvider("default")) // N.B. exceptionHandling is duplicated in resources.configure() so that // it works .exceptionHandling() .accessDeniedHandler(resources.getAccessDeniedHandler()).and() .sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS).and() .csrf().disable(); // @formatter:on 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()) { // Add anyRequest() last as a fall back. Spring Security would // replace an existing anyRequest() matcher with this one, so to // avoid that we only add it if the user hasn't configured anything. http.authorizeRequests().anyRequest().authenticated(); } }
继承ServerSecurityConfigurerAdapter,
配置了AuthenticationManager和OAuth2AuthenticationProcessingFilter(OAuth2认证处理过滤器)。
由OAuth2AuthenticationProcessingFilter通过AuthenticationManager完成身份认证。
@Override public void configure(HttpSecurity http) throws Exception { 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); http .authorizeRequests().expressionHandler(expressionHandler) .and() .addFilterBefore(resourcesServerFilter, AbstractPreAuthenticatedProcessingFilter.class) .exceptionHandling() .accessDeniedHandler(accessDeniedHandler) .authenticationEntryPoint(authenticationEntryPoint); }
创建OAuth2AuthenticationManager 为身份认证管理器。
1.设置资源id
2.设置ResourceServerTokenService,完成对应Token的认证并返回认证结果。
3.设置ClientDetailsService,验证返回的认证信息和当前配置的ClientDetails的信息是否一致。
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;
}
如果未设置ResourceServerTokenServices,则创建DefaultTokenServices(默认Token服务器)来完成用户校验。
private ResourceServerTokenServices resourceTokenServices(HttpSecurity http) { tokenServices(http); return this.resourceTokenServices; } private ResourceServerTokenServices tokenServices(HttpSecurity http) { if (resourceTokenServices != null) { return resourceTokenServices; } DefaultTokenServices tokenServices = new DefaultTokenServices(); tokenServices.setTokenStore(tokenStore()); tokenServices.setSupportRefreshToken(true); tokenServices.setClientDetailsService(clientDetails()); this.resourceTokenServices = tokenServices; return tokenServices; }
1.设置请求参数Token的值为 accessToken(访问token)
2.在请求头Authorization中设置 Basic请求头以及clientId和clientSecret的信息。用于通过BasicAuthenticationFilter完成身份认证,在通过/oauth/check_token
接口校验Token。
3.发起请求,并返回结果。
4.如果返回结果中存在error值则抛出无线访问Token异常。
5.如果返回结果中存在active为fasle,则抛出无效访问Token异常。
6.通过DefaultAccessTokenConverter完成结果转换为OAuth2Authentication
@Override public OAuth2Authentication loadAuthentication(String accessToken) throws AuthenticationException, InvalidTokenException { MultiValueMap<String, String> formData = new LinkedMultiValueMap<String, String>(); formData.add(tokenName, accessToken); HttpHeaders headers = new HttpHeaders(); headers.set("Authorization", getAuthorizationHeader(clientId, clientSecret)); Map<String, Object> map = postForMap(checkTokenEndpointUrl, formData, headers); if (map.containsKey("error")) { if (logger.isDebugEnabled()) { logger.debug("check_token returned error: " + map.get("error")); } throw new InvalidTokenException(accessToken); } // gh-838 if (!Boolean.TRUE.equals(map.get("active"))) { logger.debug("check_token returned active attribute: " + map.get("active")); throw new InvalidTokenException(accessToken); } return tokenConverter.extractAuthentication(map); }
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest)req; HttpServletResponse response = (HttpServletResponse)res; if (!this.requiresAuthentication(request, response)) { chain.doFilter(request, response); } else { if (this.logger.isDebugEnabled()) { this.logger.debug("Request is to process authentication"); } Authentication authResult; try { authResult = this.attemptAuthentication(request, response); if (authResult == null) { return; } this.sessionStrategy.onAuthentication(authResult, request, response); } catch (InternalAuthenticationServiceException var8) { this.logger.error("An internal error occurred while trying to authenticate the user.", var8); this.unsuccessfulAuthentication(request, response, var8); return; } catch (AuthenticationException var9) { this.unsuccessfulAuthentication(request, response, var9); return; } if (this.continueChainBeforeSuccessfulAuthentication) { chain.doFilter(request, response); } this.successfulAuthentication(request, response, chain, authResult); } }
该类创建由AuthorizationServerSecurityConfigurer中的clientCredentialsTokenEndpointFilter方法创建。
1.从请求参数中获取client_id 和 client_secret。
2.从请求上下文中获取当前的Authentication(认证信息),如果存在则直接返回。
3.将获取的clientId和clientSecret转换为UsernamePasswordAuthenticationToken并通过AuthenticationManager完成认证。
@Override public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException { if (allowOnlyPost && !"POST".equalsIgnoreCase(request.getMethod())) { throw new HttpRequestMethodNotSupportedException(request.getMethod(), new String[] { "POST" }); } 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; } if (clientId == null) { throw new BadCredentialsException("No client credentials presented"); } if (clientSecret == null) { clientSecret = ""; } clientId = clientId.trim(); UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(clientId, clientSecret); return this.getAuthenticationManager().authenticate(authRequest); }
在ResourceServerSecurityConfigurer中的congigure(HttpSecurity http)中配置。
1.通过TokenExtractor(Token提取器)获取Authentication(认证信息)
2.如果Authentication为空,则通过上下文判断以前是否已认证,如果已认证则清空上下文。
3.通过AuthenticationManager验证Token,并返回Authentication
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 = 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 { request.setAttribute(OAuth2AuthenticationDetails.ACCESS_TOKEN_VALUE, authentication.getPrincipal()); if (authentication instanceof AbstractAuthenticationToken) { AbstractAuthenticationToken needsDetails = (AbstractAuthenticationToken) authentication; needsDetails.setDetails(authenticationDetailsSource.buildDetails(request)); } Authentication authResult = authenticationManager.authenticate(authentication); if (debug) { logger.debug("Authentication success: " + authResult); } eventPublisher.publishAuthenticationSuccess(authResult); SecurityContextHolder.getContext().setAuthentication(authResult); } } catch (OAuth2Exception failed) { SecurityContextHolder.clearContext(); if (debug) { logger.debug("Authentication request failed: " + failed); } eventPublisher.publishAuthenticationFailure(new BadCredentialsException(failed.getMessage(), failed), new PreAuthenticatedAuthenticationToken("access-token", "N/A")); authenticationEntryPoint.commence(request, response, new InsufficientAuthenticationException(failed.getMessage(), failed)); return; } chain.doFilter(request, response); }
1.通过BasicAuthenticationConverter将请求信息(请求头存在Authorization
且,获取的值以Basic开头 username:password 这种拼接)转换为UsernamePasswordAuthenticationToken 。
2.如果认证信息为空,则过滤链继续执行。
3.根据username从上下文中取Authentication,判断是否已认证,如果已认证则继续执行。
4.如果上下文中没有则通过AuthenticationManger完成认证,并将认证信息保存在上下文中。
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException { boolean debug = this.logger.isDebugEnabled(); try { UsernamePasswordAuthenticationToken authRequest = this.authenticationConverter.convert(request); if (authRequest == null) { chain.doFilter(request, response); return; } String username = authRequest.getName(); if (debug) { this.logger.debug("Basic Authentication Authorization header found for user '" + username + "'"); } if (this.authenticationIsRequired(username)) { Authentication authResult = this.authenticationManager.authenticate(authRequest); if (debug) { this.logger.debug("Authentication success: " + authResult); } SecurityContextHolder.getContext().setAuthentication(authResult); this.rememberMeServices.loginSuccess(request, response, authResult); this.onSuccessfulAuthentication(request, response, authResult); } } catch (AuthenticationException var8) { SecurityContextHolder.clearContext(); if (debug) { this.logger.debug("Authentication request for failed!", var8); } this.rememberMeServices.loginFail(request, response); this.onUnsuccessfulAuthentication(request, response, var8); if (this.ignoreFailure) { chain.doFilter(request, response); } else { this.authenticationEntryPoint.commence(request, response, var8); } return; } chain.doFilter(request, response); }
处理身份认证请求
尝试对传递的身份验证对象进行身份验证,如果成功,则返回一个完全填充的身份验证对象(包括授予的权限)。
AuthenticationManager必须遵守以下关于异常规范:
1.如果帐户被禁用AuthenticationManager需要捕获这个状态并抛出DisabledException。
2.如果帐户被锁定AuthenticationManager需要捕获这个状态并抛出LockedException。
3.如果提供了不正确的凭据,必须抛出BadCredentialsException。虽然上述异常是可选的,但AuthenticationManager必须始终测试凭据。
应该对异常进行捕获,并在适用的情况下按上述顺序抛出异常(如果帐户被禁用或锁定,则身份验证请求立即被拒绝,授权过程不执行)。这可以防止对禁用或锁定的帐户进行授权。
Authentication authenticate(Authentication authentication)
throws AuthenticationException;
通过系列身份认证提供者完成身份认证。
/** 身份认证事件发布*/
private AuthenticationEventPublisher eventPublisher = new NullEventPublisher();
/** 系列身份认证提供者*/
private List<AuthenticationProvider> providers = Collections.emptyList();
/** 消息资源访问 */
protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();
private AuthenticationManager parent;
private boolean eraseCredentialsAfterAuthentication = true;
循环身份认证提供者,看当前身份认证提供者是否支持当前的身份认证。
如果支持,则开启认证。
在ResourceServerSecurityConfigurer中的oauthAuthenticationManager方法中创建。
1.通过ResourceTokenServices完成Token的验证并返回OAuth2Authentication。
2.如果OAuth2Authentication为空,则抛出“Invalid token”(无效Token)
3.比较当前Token返回的资源id和当前服务配置的资源id是否匹配,则抛出异常“Invalid token does not contain resource id”(无线Token不包含当前资源id)。
4.如果配置了ClientDetailsService则会校验client_id和client_secret等信息。
public Authentication authenticate(Authentication authentication) throws AuthenticationException { if (authentication == null) { throw new InvalidTokenException("Invalid token (token not found)"); } String token = (String) authentication.getPrincipal(); 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; }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。