该类是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;
}