序
本文就来讲一下spring security oauth2的几个endpoint的认证
endpoint
spring-security-oauth2-2.0.14.RELEASE-sources.jar!/org/springframework/security/oauth2/config/annotation/web/configuration/AuthorizationServerEndpointsConfiguration.java
@Configuration
@Import(TokenKeyEndpointRegistrar.class)
public class AuthorizationServerEndpointsConfiguration {
private AuthorizationServerEndpointsConfigurer endpoints = new AuthorizationServerEndpointsConfigurer();
@Autowired
private ClientDetailsService clientDetailsService;
@Autowired
private List<AuthorizationServerConfigurer> configurers = Collections.emptyList();
@PostConstruct
public void init() {
for (AuthorizationServerConfigurer configurer : configurers) {
try {
configurer.configure(endpoints);
} catch (Exception e) {
throw new IllegalStateException("Cannot configure enpdoints", e);
}
}
endpoints.setClientDetailsService(clientDetailsService);
}
@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());
return authorizationEndpoint;
}
@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();
}
//......
}
这里显示配置了如下几个
- /oauth/authorize
- /oauth/token
- /oauth/check_token
- /oauth/confirm_access
- /oauth/error
endpoint的认证
/oauth/authorize
这个需要保护用户账号密码认证保护,否则会报
curl -i http://localhost:8080/oauth/authorize\?response_type\=code\&client_id\=demoApp\&redirect_uri\=http://localhost:8081/callback
HTTP/1.1 500
X-Application-Context: application
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Sun, 10 Dec 2017 07:12:50 GMT
Connection: close
{"timestamp":1512889970484,"status":500,"error":"Internal Server Error","exception":"org.springframework.security.authentication.InsufficientAuthenticationException","message":"User must be authenticated with Spring Security before authorization can be completed.","path":"/oauth/authorize"}
/oauth/token
- 这个如果配置支持allowFormAuthenticationForClients的,且url中有client_id和client_secret的会走ClientCredentialsTokenEndpointFilter来保护
- 如果没有支持allowFormAuthenticationForClients或者有支持但是url中没有client_id和client_secret的,走basic认证保护
/oauth/check_token
这个走basic认证保护
/oauth/confirm_access
这个需要认证保护,否则报500
curl -i http://localhost:8080/oauth/confirm_access
HTTP/1.1 500
X-Application-Context: application
Cache-Control: no-store
Content-Type: application/json;charset=UTF-8
Content-Language: zh-CN
Transfer-Encoding: chunked
Date: Sun, 10 Dec 2017 07:13:55 GMT
Connection: close
{"timestamp":1512890035907,"status":500,"error":"Internal Server Error","exception":"org.springframework.expression.spel.SpelEvaluationException","message":"EL1008E: Property or field 'authorizationRequest' cannot be found on object of type 'java.util.HashMap' - maybe not public?","path":"/oauth/confirm_access"}
/oauth/error
这个可以不用认证保护
basic认证保护的源码
spring-security-oauth2-2.0.14.RELEASE-sources.jar!/org/springframework/security/oauth2/config/annotation/web/configuration/AuthorizationServerSecurityConfiguration.java
@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);
}
其中,可以自己指定check_token的认证级别,而/oauth/token则需要fullyAuthenticated
token_key这个是jwt特有的,这里忽略不讲
isAuthenticated()与isFullyAuthenticated的区别
一个是authenticated,一个是fullyAuthenticated,前者排除anonymous,后者排除anonymous以及remember-me
- isAuthenticated()
Returns true if the user is not anonymous
- isFullyAuthenticated()
Returns true if the user is not an anonymous or a remember-me user
小结
综上,需要保护/oauth/authorize以及/oauth/confirm_access这两个endpoint,当然主要是/oauth/authorize这个。
由于其他几个/oauth/开头的认证endpoint配置的认证优先级高于默认的WebSecurityConfigurerAdapter配置(order=100),因此默认的可以这样配置
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http
.requestMatchers().antMatchers("/oauth/**","/login/**","/logout/**")
.and()
.authorizeRequests()
.antMatchers("/oauth/**").authenticated()
.and()
.formLogin().permitAll(); //新增login form支持用户登录及授权
}
}
把整个/oauth/**保护进来