Spring 具有传统 Spring MVC 的安全 OAuth 2
Spring Security OAuth 2 with traditional Spring MVC
为传统的非引导 spring mvc 应用程序设置 spring-security-oauth2(密码授予类型)时出现问题。
能够检索访问令牌(第 1 步)。
使用 'access token' 检索受保护资源时出现状态 401(请参阅第 2 步)。
卷曲命令:
获取访问令牌:
curl -X POST --user clientapp:123456 http://localhost:8080/oauth/token -H "accept: application/json" -H "content-type: application/x-www-form-urlencoded" -d "grant_type=password&username=adolfo&password=123&scope=read_profile"
响应:
{"access_token":"50c7c311-c73a-4a32-bc7e-6801bc64bbe0","token_type":"bearer","expires_in":41545,"scope":"read_profile"}
使用访问令牌检索受保护的数据
curl -X GET http://localhost:8080/api/profile -H "authorization: Bearer 50c7c311-c73a-4a32-bc7e-6801bc64bbe0"
响应:状态 401
The request has not been applied because it lacks valid authentication credentials for the target resource.
配置
//资源服务器配置
@Configuration
@EnableResourceServer
public class OAuth2ResourceServer extends ResourceServerConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
//@formatter:off
http.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.requestMatchers()
.antMatchers("/api/**");
//@formatter:on
}
}
授权服务器配置
@Configuration
@EnableAuthorizationServer
public class OAuth2AuthorizationServer extends AuthorizationServerConfigurerAdapter {
@Autowired
private AuthenticationManager authenticationManager;
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager);
}
@Override
public void configure(ClientDetailsServiceConfigurer clients)
throws Exception {
clients.inMemory()
.withClient("clientapp")
.secret("123456")
//.autoApprove(true)
.redirectUris("http://localhost:9000/callback")
.authorizedGrantTypes("password")
.scopes("read_profile", "read_contacts");
}
@Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer.passwordEncoder(NoOpPasswordEncoder.getInstance());
}
}
安全配置
Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/api/**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.usernameParameter("username")
.passwordParameter("password")
.loginPage("/signin")
.loginProcessingUrl("/authenticate")
.defaultSuccessUrl("/")
.failureUrl("/signin-error")
.permitAll();
}
@Bean(name = BeanIds.AUTHENTICATION_MANAGER)
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().passwordEncoder(NoOpPasswordEncoder.getInstance())
//.passwordEncoder(passwordEncoder())
.withUser("adolfo")
.password("123")
.roles("USER");
}
}
//控制器
@Controller
public class UserController {
@RequestMapping("/api/profile")
public ResponseEntity<UserProfile> profile() {
String username = (String) SecurityContextHolder.getContext()
.getAuthentication().getPrincipal();
String email = username + "@mailinator.com";
UserProfile profile = new UserProfile();
profile.setName(username);
profile.setEmail(email);
return ResponseEntity.ok(profile);
}
初始化Class
public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[]{ SecurityConfig.class, OAuth2AuthorizationServer.class, OAuth2ResourceServer.class,};
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[]{DispatcherConfig.class};
}
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
@Override
protected Filter[] getServletFilters() {
DelegatingFilterProxy delegatingFilterProxy = new DelegatingFilterProxy("springSecurityFilterChain");
HiddenHttpMethodFilter hiddenHttpMethodFilter = new HiddenHttpMethodFilter();
return new Filter[] {delegatingFilterProxy,hiddenHttpMethodFilter};
}
}
//日志
2018-08-13 15:50:29,333 DEBUG [http-nio-8080-exec-10] o.s.s.w.u.m.OrRequestMatcher [OrRequestMatcher.java:65] Trying to match using Ant [pattern='/oauth/token']
2018-08-13 15:50:29,334 DEBUG [http-nio-8080-exec-10] o.s.s.w.u.m.AntPathRequestMatcher [AntPathRequestMatcher.java:157] Checking match of request : '/api/profile'; against '/oauth/token'
2018-08-13 15:50:29,334 DEBUG [http-nio-8080-exec-10] o.s.s.w.u.m.OrRequestMatcher [OrRequestMatcher.java:65] Trying to match using Ant [pattern='/oauth/token_key']
2018-08-13 15:50:29,334 DEBUG [http-nio-8080-exec-10] o.s.s.w.u.m.AntPathRequestMatcher [AntPathRequestMatcher.java:157] Checking match of request : '/api/profile'; against '/oauth/token_key'
2018-08-13 15:50:29,334 DEBUG [http-nio-8080-exec-10] o.s.s.w.u.m.OrRequestMatcher [OrRequestMatcher.java:65] Trying to match using Ant [pattern='/oauth/check_token']
2018-08-13 15:50:29,334 DEBUG [http-nio-8080-exec-10] o.s.s.w.u.m.AntPathRequestMatcher [AntPathRequestMatcher.java:157] Checking match of request : '/api/profile'; against '/oauth/check_token'
2018-08-13 15:50:29,334 DEBUG [http-nio-8080-exec-10] o.s.s.w.u.m.OrRequestMatcher [OrRequestMatcher.java:72] No matches found
2018-08-13 15:50:29,334 DEBUG [http-nio-8080-exec-10] o.s.s.w.u.m.OrRequestMatcher [OrRequestMatcher.java:65] Trying to match using Ant [pattern='/api/**']
2018-08-13 15:50:29,335 DEBUG [http-nio-8080-exec-10] o.s.s.w.u.m.AntPathRequestMatcher [AntPathRequestMatcher.java:157] Checking match of request : '/api/profile'; against '/api/**'
2018-08-13 15:50:29,335 DEBUG [http-nio-8080-exec-10] o.s.s.w.u.m.OrRequestMatcher [OrRequestMatcher.java:68] matched
2018-08-13 15:50:29,335 DEBUG [http-nio-8080-exec-10] o.s.s.w.FilterChainProxy [FilterChainProxy.java:328] /api/profile at position 1 of 11 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
2018-08-13 15:50:29,335 DEBUG [http-nio-8080-exec-10] o.s.s.w.FilterChainProxy [FilterChainProxy.java:328] /api/profile at position 2 of 11 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
2018-08-13 15:50:29,335 DEBUG [http-nio-8080-exec-10] o.s.s.w.FilterChainProxy [FilterChainProxy.java:328] /api/profile at position 3 of 11 in additional filter chain; firing Filter: 'HeaderWriterFilter'
2018-08-13 15:50:29,335 DEBUG [http-nio-8080-exec-10] o.s.s.w.FilterChainProxy [FilterChainProxy.java:328] /api/profile at position 4 of 11 in additional filter chain; firing Filter: 'LogoutFilter'
2018-08-13 15:50:29,335 DEBUG [http-nio-8080-exec-10] o.s.s.w.u.m.OrRequestMatcher [OrRequestMatcher.java:65] Trying to match using Ant [pattern='/logout', GET]
2018-08-13 15:50:29,335 DEBUG [http-nio-8080-exec-10] o.s.s.w.u.m.AntPathRequestMatcher [AntPathRequestMatcher.java:157] Checking match of request : '/api/profile'; against '/logout'
2018-08-13 15:50:29,336 DEBUG [http-nio-8080-exec-10] o.s.s.w.u.m.OrRequestMatcher [OrRequestMatcher.java:65] Trying to match using Ant [pattern='/logout', POST]
2018-08-13 15:50:29,336 DEBUG [http-nio-8080-exec-10] o.s.s.w.u.m.AntPathRequestMatcher [AntPathRequestMatcher.java:137] Request 'GET /api/profile' doesn't match 'POST /logout
2018-08-13 15:50:29,336 DEBUG [http-nio-8080-exec-10] o.s.s.w.u.m.OrRequestMatcher [OrRequestMatcher.java:65] Trying to match using Ant [pattern='/logout', PUT]
2018-08-13 15:50:29,336 DEBUG [http-nio-8080-exec-10] o.s.s.w.u.m.AntPathRequestMatcher [AntPathRequestMatcher.java:137] Request 'GET /api/profile' doesn't match 'PUT /logout
2018-08-13 15:50:29,336 DEBUG [http-nio-8080-exec-10] o.s.s.w.u.m.OrRequestMatcher [OrRequestMatcher.java:65] Trying to match using Ant [pattern='/logout', DELETE]
2018-08-13 15:50:29,336 DEBUG [http-nio-8080-exec-10] o.s.s.w.u.m.AntPathRequestMatcher [AntPathRequestMatcher.java:137] Request 'GET /api/profile' doesn't match 'DELETE /logout
2018-08-13 15:50:29,337 DEBUG [http-nio-8080-exec-10] o.s.s.w.u.m.OrRequestMatcher [OrRequestMatcher.java:72] No matches found
2018-08-13 15:50:29,337 DEBUG [http-nio-8080-exec-10] o.s.s.w.FilterChainProxy [FilterChainProxy.java:328] /api/profile at position 5 of 11 in additional filter chain; firing Filter: 'OAuth2AuthenticationProcessingFilter'
2018-08-13 15:50:29,347 DEBUG [http-nio-8080-exec-10] o.s.s.o.p.a.OAuth2AuthenticationProcessingFilter [OAuth2AuthenticationProcessingFilter.java:165] Authentication request failed: error="invalid_token", error_description="Invalid access token: d86d8105-9edf-44dd-94b6-36542cade80f"
2018-08-13 15:50:29,365 DEBUG [http-nio-8080-exec-10] o.s.s.w.h.w.HstsHeaderWriter [HstsHeaderWriter.java:129] Not injecting HSTS header since it did not match the requestMatcher org.springframework.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher@5337f7a
2018-08-13 15:50:29,365 DEBUG [http-nio-8080-exec-10] o.s.s.o.p.e.DefaultOAuth2ExceptionRenderer [DefaultOAuth2ExceptionRenderer.java:101] Written [error="invalid_token", error_description="Invalid access token: d86d8105-9edf-44dd-94b6-36542cade80f"] as "application/json;charset=UTF-8" using [org.springframework.http.converter.json.MappingJackson2HttpMessageConverter@734d0eb7]
2018-08-13 15:50:29,366 DEBUG [http-nio-8080-exec-10] o.s.s.w.c.SecurityContextPersistenceFilter [SecurityContextPersistenceFilter.java:119] SecurityContextHolder now cleared, as request processing completed
查看整个日志here
我在你的堆栈跟踪中没有看到 OAuth2AuthenticationProcessingFilter
。我认为这是因为 @Order(2)
注释放在 SecurityConfig
class 上。您的网络安全配置与 Spring 的内部配置 (org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfiguration
) 冲突。尝试从 SecurityConfig
class.
中删除 @Order
注释
编辑
获得 401 响应的另一个原因是使用 org.springframework.security.oauth2.provider.token.TokenStore
的不同实例:一个用于存储新创建的令牌,另一个用于身份验证。这可能是由于 org.springframework.web.servlet.config.annotation.WebMvcConfigurer
的实现引用(例如通过 ComponentScan
注释)到已声明为根配置 classes 的其他配置时,servlet 初始化程序的错误配置引起的。它会导致创建两个相互冲突的应用程序上下文。
假设您有三种配置 classes:
ServletConfig
实现 org.springframework.web.servlet.config.annotation.WebMvcConfigurer
;
AuthorizationServerConfig
;
ResourceServerConfig
.
有两种配置 servlet 初始值设定项的方法。第一个是 return ServletConfig
class 从 getServletConfigClasses
方法:
public class MyServletInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[] {AuthorizationServerConfig.class, AuthorizationServerConfig.class};
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[] {ServletConfig.class};
}
}
通过选择这种方式,您应该确保不将根配置中的 classes 包含到 servlet 配置中(不要 运行 来自 ServletConfig
class 的组件扫描针对根配置 classes 所在的包)。
第二种方法是将 ServletConfig
移动到根配置 classes returning null
from getServletConfigClasses
:
public class MyServletInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[] {ServletConfig.class, AuthorizationServerConfig.class, AuthorizationServerConfig.class};
}
@Override
protected Class<?>[] getServletConfigClasses() {
return null;
}
}
只要不需要多个dispatcher servlet,选择第二种方式完全没问题。
为传统的非引导 spring mvc 应用程序设置 spring-security-oauth2(密码授予类型)时出现问题。
能够检索访问令牌(第 1 步)。
使用 'access token' 检索受保护资源时出现状态 401(请参阅第 2 步)。
卷曲命令:
获取访问令牌:
curl -X POST --user clientapp:123456 http://localhost:8080/oauth/token -H "accept: application/json" -H "content-type: application/x-www-form-urlencoded" -d "grant_type=password&username=adolfo&password=123&scope=read_profile"
响应:
{"access_token":"50c7c311-c73a-4a32-bc7e-6801bc64bbe0","token_type":"bearer","expires_in":41545,"scope":"read_profile"}
使用访问令牌检索受保护的数据
curl -X GET http://localhost:8080/api/profile -H "authorization: Bearer 50c7c311-c73a-4a32-bc7e-6801bc64bbe0"
响应:状态 401
The request has not been applied because it lacks valid authentication credentials for the target resource.
配置
//资源服务器配置
@Configuration
@EnableResourceServer
public class OAuth2ResourceServer extends ResourceServerConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
//@formatter:off
http.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.requestMatchers()
.antMatchers("/api/**");
//@formatter:on
}
}
授权服务器配置
@Configuration
@EnableAuthorizationServer
public class OAuth2AuthorizationServer extends AuthorizationServerConfigurerAdapter {
@Autowired
private AuthenticationManager authenticationManager;
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager);
}
@Override
public void configure(ClientDetailsServiceConfigurer clients)
throws Exception {
clients.inMemory()
.withClient("clientapp")
.secret("123456")
//.autoApprove(true)
.redirectUris("http://localhost:9000/callback")
.authorizedGrantTypes("password")
.scopes("read_profile", "read_contacts");
}
@Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer.passwordEncoder(NoOpPasswordEncoder.getInstance());
}
}
安全配置
Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/api/**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.usernameParameter("username")
.passwordParameter("password")
.loginPage("/signin")
.loginProcessingUrl("/authenticate")
.defaultSuccessUrl("/")
.failureUrl("/signin-error")
.permitAll();
}
@Bean(name = BeanIds.AUTHENTICATION_MANAGER)
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().passwordEncoder(NoOpPasswordEncoder.getInstance())
//.passwordEncoder(passwordEncoder())
.withUser("adolfo")
.password("123")
.roles("USER");
}
}
//控制器
@Controller
public class UserController {
@RequestMapping("/api/profile")
public ResponseEntity<UserProfile> profile() {
String username = (String) SecurityContextHolder.getContext()
.getAuthentication().getPrincipal();
String email = username + "@mailinator.com";
UserProfile profile = new UserProfile();
profile.setName(username);
profile.setEmail(email);
return ResponseEntity.ok(profile);
}
初始化Class
public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[]{ SecurityConfig.class, OAuth2AuthorizationServer.class, OAuth2ResourceServer.class,};
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[]{DispatcherConfig.class};
}
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
@Override
protected Filter[] getServletFilters() {
DelegatingFilterProxy delegatingFilterProxy = new DelegatingFilterProxy("springSecurityFilterChain");
HiddenHttpMethodFilter hiddenHttpMethodFilter = new HiddenHttpMethodFilter();
return new Filter[] {delegatingFilterProxy,hiddenHttpMethodFilter};
}
}
//日志
2018-08-13 15:50:29,333 DEBUG [http-nio-8080-exec-10] o.s.s.w.u.m.OrRequestMatcher [OrRequestMatcher.java:65] Trying to match using Ant [pattern='/oauth/token']
2018-08-13 15:50:29,334 DEBUG [http-nio-8080-exec-10] o.s.s.w.u.m.AntPathRequestMatcher [AntPathRequestMatcher.java:157] Checking match of request : '/api/profile'; against '/oauth/token'
2018-08-13 15:50:29,334 DEBUG [http-nio-8080-exec-10] o.s.s.w.u.m.OrRequestMatcher [OrRequestMatcher.java:65] Trying to match using Ant [pattern='/oauth/token_key']
2018-08-13 15:50:29,334 DEBUG [http-nio-8080-exec-10] o.s.s.w.u.m.AntPathRequestMatcher [AntPathRequestMatcher.java:157] Checking match of request : '/api/profile'; against '/oauth/token_key'
2018-08-13 15:50:29,334 DEBUG [http-nio-8080-exec-10] o.s.s.w.u.m.OrRequestMatcher [OrRequestMatcher.java:65] Trying to match using Ant [pattern='/oauth/check_token']
2018-08-13 15:50:29,334 DEBUG [http-nio-8080-exec-10] o.s.s.w.u.m.AntPathRequestMatcher [AntPathRequestMatcher.java:157] Checking match of request : '/api/profile'; against '/oauth/check_token'
2018-08-13 15:50:29,334 DEBUG [http-nio-8080-exec-10] o.s.s.w.u.m.OrRequestMatcher [OrRequestMatcher.java:72] No matches found
2018-08-13 15:50:29,334 DEBUG [http-nio-8080-exec-10] o.s.s.w.u.m.OrRequestMatcher [OrRequestMatcher.java:65] Trying to match using Ant [pattern='/api/**']
2018-08-13 15:50:29,335 DEBUG [http-nio-8080-exec-10] o.s.s.w.u.m.AntPathRequestMatcher [AntPathRequestMatcher.java:157] Checking match of request : '/api/profile'; against '/api/**'
2018-08-13 15:50:29,335 DEBUG [http-nio-8080-exec-10] o.s.s.w.u.m.OrRequestMatcher [OrRequestMatcher.java:68] matched
2018-08-13 15:50:29,335 DEBUG [http-nio-8080-exec-10] o.s.s.w.FilterChainProxy [FilterChainProxy.java:328] /api/profile at position 1 of 11 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
2018-08-13 15:50:29,335 DEBUG [http-nio-8080-exec-10] o.s.s.w.FilterChainProxy [FilterChainProxy.java:328] /api/profile at position 2 of 11 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
2018-08-13 15:50:29,335 DEBUG [http-nio-8080-exec-10] o.s.s.w.FilterChainProxy [FilterChainProxy.java:328] /api/profile at position 3 of 11 in additional filter chain; firing Filter: 'HeaderWriterFilter'
2018-08-13 15:50:29,335 DEBUG [http-nio-8080-exec-10] o.s.s.w.FilterChainProxy [FilterChainProxy.java:328] /api/profile at position 4 of 11 in additional filter chain; firing Filter: 'LogoutFilter'
2018-08-13 15:50:29,335 DEBUG [http-nio-8080-exec-10] o.s.s.w.u.m.OrRequestMatcher [OrRequestMatcher.java:65] Trying to match using Ant [pattern='/logout', GET]
2018-08-13 15:50:29,335 DEBUG [http-nio-8080-exec-10] o.s.s.w.u.m.AntPathRequestMatcher [AntPathRequestMatcher.java:157] Checking match of request : '/api/profile'; against '/logout'
2018-08-13 15:50:29,336 DEBUG [http-nio-8080-exec-10] o.s.s.w.u.m.OrRequestMatcher [OrRequestMatcher.java:65] Trying to match using Ant [pattern='/logout', POST]
2018-08-13 15:50:29,336 DEBUG [http-nio-8080-exec-10] o.s.s.w.u.m.AntPathRequestMatcher [AntPathRequestMatcher.java:137] Request 'GET /api/profile' doesn't match 'POST /logout
2018-08-13 15:50:29,336 DEBUG [http-nio-8080-exec-10] o.s.s.w.u.m.OrRequestMatcher [OrRequestMatcher.java:65] Trying to match using Ant [pattern='/logout', PUT]
2018-08-13 15:50:29,336 DEBUG [http-nio-8080-exec-10] o.s.s.w.u.m.AntPathRequestMatcher [AntPathRequestMatcher.java:137] Request 'GET /api/profile' doesn't match 'PUT /logout
2018-08-13 15:50:29,336 DEBUG [http-nio-8080-exec-10] o.s.s.w.u.m.OrRequestMatcher [OrRequestMatcher.java:65] Trying to match using Ant [pattern='/logout', DELETE]
2018-08-13 15:50:29,336 DEBUG [http-nio-8080-exec-10] o.s.s.w.u.m.AntPathRequestMatcher [AntPathRequestMatcher.java:137] Request 'GET /api/profile' doesn't match 'DELETE /logout
2018-08-13 15:50:29,337 DEBUG [http-nio-8080-exec-10] o.s.s.w.u.m.OrRequestMatcher [OrRequestMatcher.java:72] No matches found
2018-08-13 15:50:29,337 DEBUG [http-nio-8080-exec-10] o.s.s.w.FilterChainProxy [FilterChainProxy.java:328] /api/profile at position 5 of 11 in additional filter chain; firing Filter: 'OAuth2AuthenticationProcessingFilter'
2018-08-13 15:50:29,347 DEBUG [http-nio-8080-exec-10] o.s.s.o.p.a.OAuth2AuthenticationProcessingFilter [OAuth2AuthenticationProcessingFilter.java:165] Authentication request failed: error="invalid_token", error_description="Invalid access token: d86d8105-9edf-44dd-94b6-36542cade80f"
2018-08-13 15:50:29,365 DEBUG [http-nio-8080-exec-10] o.s.s.w.h.w.HstsHeaderWriter [HstsHeaderWriter.java:129] Not injecting HSTS header since it did not match the requestMatcher org.springframework.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher@5337f7a
2018-08-13 15:50:29,365 DEBUG [http-nio-8080-exec-10] o.s.s.o.p.e.DefaultOAuth2ExceptionRenderer [DefaultOAuth2ExceptionRenderer.java:101] Written [error="invalid_token", error_description="Invalid access token: d86d8105-9edf-44dd-94b6-36542cade80f"] as "application/json;charset=UTF-8" using [org.springframework.http.converter.json.MappingJackson2HttpMessageConverter@734d0eb7]
2018-08-13 15:50:29,366 DEBUG [http-nio-8080-exec-10] o.s.s.w.c.SecurityContextPersistenceFilter [SecurityContextPersistenceFilter.java:119] SecurityContextHolder now cleared, as request processing completed
查看整个日志here
我在你的堆栈跟踪中没有看到 OAuth2AuthenticationProcessingFilter
。我认为这是因为 @Order(2)
注释放在 SecurityConfig
class 上。您的网络安全配置与 Spring 的内部配置 (org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfiguration
) 冲突。尝试从 SecurityConfig
class.
@Order
注释
编辑
获得 401 响应的另一个原因是使用 org.springframework.security.oauth2.provider.token.TokenStore
的不同实例:一个用于存储新创建的令牌,另一个用于身份验证。这可能是由于 org.springframework.web.servlet.config.annotation.WebMvcConfigurer
的实现引用(例如通过 ComponentScan
注释)到已声明为根配置 classes 的其他配置时,servlet 初始化程序的错误配置引起的。它会导致创建两个相互冲突的应用程序上下文。
假设您有三种配置 classes:
ServletConfig
实现org.springframework.web.servlet.config.annotation.WebMvcConfigurer
;AuthorizationServerConfig
;ResourceServerConfig
.
有两种配置 servlet 初始值设定项的方法。第一个是 return ServletConfig
class 从 getServletConfigClasses
方法:
public class MyServletInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[] {AuthorizationServerConfig.class, AuthorizationServerConfig.class};
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[] {ServletConfig.class};
}
}
通过选择这种方式,您应该确保不将根配置中的 classes 包含到 servlet 配置中(不要 运行 来自 ServletConfig
class 的组件扫描针对根配置 classes 所在的包)。
第二种方法是将 ServletConfig
移动到根配置 classes returning null
from getServletConfigClasses
:
public class MyServletInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[] {ServletConfig.class, AuthorizationServerConfig.class, AuthorizationServerConfig.class};
}
@Override
protected Class<?>[] getServletConfigClasses() {
return null;
}
}
只要不需要多个dispatcher servlet,选择第二种方式完全没问题。