在接入oauth2之後可以訪問相關oauth2接口
[/oauth/authorize] [/oauth/token] [/oauth/check_token] [/oauth/confirm_access] [/oauth/token_key] [/oauth/error]
會好奇是如何實現的。其實內部是基於spring mvc
我們搭建oauth2服務器會有以下配置
@Configuration @EnableAuthorizationServer//<1>具體端點初始化在這裏 public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter { ....... }
<1>
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented //<2>AuthorizationServerEndpointsConfiguration 爲端點初始化自動化配置類 @Import({AuthorizationServerEndpointsConfiguration.class, AuthorizationServerSecurityConfiguration.class}) public @interface EnableAuthorizationServer { }
<2>
@Configuration @Import({TokenKeyEndpointRegistrar.class}) public class AuthorizationServerEndpointsConfiguration { private AuthorizationServerEndpointsConfigurer endpoints = new AuthorizationServerEndpointsConfigurer(); @Autowired private ClientDetailsService clientDetailsService; @Autowired private List<AuthorizationServerConfigurer> configurers = Collections.emptyList(); public AuthorizationServerEndpointsConfiguration() { } //<3> /oauth/authorize spring mvchandler @Bean public AuthorizationEndpoint authorizationEndpoint() throws Exception { AuthorizationEndpoint authorizationEndpoint = new AuthorizationEndpoint(); FrameworkEndpointHandlerMapping mapping = this.getEndpointsConfigurer().getFrameworkEndpointHandlerMapping(); authorizationEndpoint.setUserApprovalPage(this.extractPath(mapping, "/oauth/confirm_access")); authorizationEndpoint.setProviderExceptionHandler(this.exceptionTranslator()); authorizationEndpoint.setErrorPage(this.extractPath(mapping, "/oauth/error")); authorizationEndpoint.setTokenGranter(this.tokenGranter()); authorizationEndpoint.setClientDetailsService(this.clientDetailsService); authorizationEndpoint.setAuthorizationCodeServices(this.authorizationCodeServices()); authorizationEndpoint.setOAuth2RequestFactory(this.oauth2RequestFactory()); authorizationEndpoint.setOAuth2RequestValidator(this.oauth2RequestValidator()); authorizationEndpoint.setUserApprovalHandler(this.userApprovalHandler()); return authorizationEndpoint; } // /oauth/token spring mvchandler @Bean public TokenEndpoint tokenEndpoint() throws Exception { TokenEndpoint tokenEndpoint = new TokenEndpoint(); tokenEndpoint.setClientDetailsService(this.clientDetailsService); tokenEndpoint.setProviderExceptionHandler(this.exceptionTranslator()); tokenEndpoint.setTokenGranter(this.tokenGranter()); tokenEndpoint.setOAuth2RequestFactory(this.oauth2RequestFactory()); tokenEndpoint.setOAuth2RequestValidator(this.oauth2RequestValidator()); tokenEndpoint.setAllowedRequestMethods(this.allowedTokenEndpointRequestMethods()); return tokenEndpoint; } // /oauth/check_token spring mvchandler @Bean public CheckTokenEndpoint checkTokenEndpoint() { CheckTokenEndpoint endpoint = new CheckTokenEndpoint(this.getEndpointsConfigurer().getResourceServerTokenServices()); endpoint.setAccessTokenConverter(this.getEndpointsConfigurer().getAccessTokenConverter()); endpoint.setExceptionTranslator(this.exceptionTranslator()); return endpoint; } // /oauth/confirm_access 端點 @Bean public WhitelabelApprovalEndpoint whitelabelApprovalEndpoint() { return new WhitelabelApprovalEndpoint(); } // /oauth/error 端點 @Bean public WhitelabelErrorEndpoint whitelabelErrorEndpoint() { return new WhitelabelErrorEndpoint(); } //<3>spring mvc RequestMappingHandlerMapping 擴展暴露端點 @Bean public FrameworkEndpointHandlerMapping oauth2EndpointHandlerMapping() throws Exception { return this.getEndpointsConfigurer().getFrameworkEndpointHandlerMapping(); } }
<4>
public class FrameworkEndpointHandlerMapping extends RequestMappingHandlerMapping { //<5>匹配端點 protected boolean isHandler(Class<?> beanType) { return AnnotationUtils.findAnnotation(beanType, FrameworkEndpoint.class) != null; } }
<5>
// // Source code recreated from a .class file by IntelliJ IDEA // (powered by FernFlower decompiler) // package org.springframework.security.oauth2.provider.endpoint; import java.security.Principal; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.security.authentication.InsufficientAuthenticationException; import org.springframework.security.core.Authentication; import org.springframework.security.oauth2.common.OAuth2AccessToken; import org.springframework.security.oauth2.common.exceptions.BadClientCredentialsException; import org.springframework.security.oauth2.common.exceptions.InvalidClientException; import org.springframework.security.oauth2.common.exceptions.InvalidGrantException; import org.springframework.security.oauth2.common.exceptions.InvalidRequestException; import org.springframework.security.oauth2.common.exceptions.OAuth2Exception; import org.springframework.security.oauth2.common.exceptions.UnsupportedGrantTypeException; import org.springframework.security.oauth2.common.util.OAuth2Utils; import org.springframework.security.oauth2.provider.ClientDetails; import org.springframework.security.oauth2.provider.ClientRegistrationException; import org.springframework.security.oauth2.provider.OAuth2Authentication; import org.springframework.security.oauth2.provider.OAuth2RequestValidator; import org.springframework.security.oauth2.provider.TokenRequest; import org.springframework.security.oauth2.provider.request.DefaultOAuth2RequestValidator; import org.springframework.util.StringUtils; import org.springframework.web.HttpRequestMethodNotSupportedException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; @FrameworkEndpoint //oauth2端點標記 public class TokenEndpoint extends AbstractEndpoint { private OAuth2RequestValidator oAuth2RequestValidator = new DefaultOAuth2RequestValidator(); private Set<HttpMethod> allowedRequestMethods; public TokenEndpoint() { this.allowedRequestMethods = new HashSet(Arrays.asList(HttpMethod.POST)); } @RequestMapping( value = {"/oauth/token"}, method = {RequestMethod.GET} ) public ResponseEntity<OAuth2AccessToken> getAccessToken(Principal principal, @RequestParam Map<String, String> parameters) throws HttpRequestMethodNotSupportedException { if (!this.allowedRequestMethods.contains(HttpMethod.GET)) { throw new HttpRequestMethodNotSupportedException("GET"); } else { return this.postAccessToken(principal, parameters); } } @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."); } else { String clientId = this.getClientId(principal); ClientDetails authenticatedClient = this.getClientDetailsService().loadClientByClientId(clientId); TokenRequest tokenRequest = this.getOAuth2RequestFactory().createTokenRequest(parameters, authenticatedClient); if (clientId != null && !clientId.equals("") && !clientId.equals(tokenRequest.getClientId())) { throw new InvalidClientException("Given client ID does not match authenticated client"); } else { if (authenticatedClient != null) { this.oAuth2RequestValidator.validateScope(tokenRequest, authenticatedClient); } if (!StringUtils.hasText(tokenRequest.getGrantType())) { throw new InvalidRequestException("Missing grant type"); } else if (tokenRequest.getGrantType().equals("implicit")) { throw new InvalidGrantException("Implicit grant type not supported from token endpoint"); } else { if (this.isAuthCodeRequest(parameters) && !tokenRequest.getScope().isEmpty()) { this.logger.debug("Clearing scope of incoming token request"); tokenRequest.setScope(Collections.emptySet()); } if (this.isRefreshTokenRequest(parameters)) { tokenRequest.setScope(OAuth2Utils.parseParameterList((String)parameters.get("scope"))); } OAuth2AccessToken token = this.getTokenGranter().grant(tokenRequest.getGrantType(), tokenRequest); if (token == null) { throw new UnsupportedGrantTypeException("Unsupported grant type"); } else { return this.getResponse(token); } } } } } protected String getClientId(Principal principal) { Authentication client = (Authentication)principal; if (!client.isAuthenticated()) { throw new InsufficientAuthenticationException("The client is not authenticated."); } else { String clientId = client.getName(); if (client instanceof OAuth2Authentication) { clientId = ((OAuth2Authentication)client).getOAuth2Request().getClientId(); } return clientId; } } @ExceptionHandler({HttpRequestMethodNotSupportedException.class}) public ResponseEntity<OAuth2Exception> handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e) throws Exception { if (this.logger.isInfoEnabled()) { this.logger.info("Handling error: " + e.getClass().getSimpleName() + ", " + e.getMessage()); } return this.getExceptionTranslator().translate(e); } @ExceptionHandler({Exception.class}) public ResponseEntity<OAuth2Exception> handleException(Exception e) throws Exception { if (this.logger.isErrorEnabled()) { this.logger.error("Handling error: " + e.getClass().getSimpleName() + ", " + e.getMessage(), e); } return this.getExceptionTranslator().translate(e); } @ExceptionHandler({ClientRegistrationException.class}) public ResponseEntity<OAuth2Exception> handleClientRegistrationException(Exception e) throws Exception { if (this.logger.isWarnEnabled()) { this.logger.warn("Handling error: " + e.getClass().getSimpleName() + ", " + e.getMessage()); } return this.getExceptionTranslator().translate(new BadClientCredentialsException()); } @ExceptionHandler({OAuth2Exception.class}) public ResponseEntity<OAuth2Exception> handleException(OAuth2Exception e) throws Exception { if (this.logger.isWarnEnabled()) { this.logger.warn("Handling error: " + e.getClass().getSimpleName() + ", " + e.getMessage()); } return this.getExceptionTranslator().translate(e); } private ResponseEntity<OAuth2AccessToken> getResponse(OAuth2AccessToken accessToken) { HttpHeaders headers = new HttpHeaders(); headers.set("Cache-Control", "no-store"); headers.set("Pragma", "no-cache"); return new ResponseEntity(accessToken, headers, HttpStatus.OK); } private boolean isRefreshTokenRequest(Map<String, String> parameters) { return "refresh_token".equals(parameters.get("grant_type")) && parameters.get("refresh_token") != null; } private boolean isAuthCodeRequest(Map<String, String> parameters) { return "authorization_code".equals(parameters.get("grant_type")) && parameters.get("code") != null; } public void setOAuth2RequestValidator(OAuth2RequestValidator oAuth2RequestValidator) { this.oAuth2RequestValidator = oAuth2RequestValidator; } public void setAllowedRequestMethods(Set<HttpMethod> allowedRequestMethods) { this.allowedRequestMethods = allowedRequestMethods; } }