• 错误处理: oauth2 client访问oauth2 server 的user info 端点, 返回401 invalid_user_info_response


    oauth2 client访问oauth2 server 的user info 端点; 返回401 invalid_user_info_response

    日志是: invalid_user_info_response] An error occurred while attempting to retrieve the UserInfo Resource: 401 null]
    onAuthenticationFailure request = [org.springframework.security.web.header.HeaderWriterFilter$HeaderWriterRequest@50eeb3fd], response = [org.springframework.security.web.header.HeaderWriterFilter$HeaderWriterResponse@5e294790], exception = [org.springframework.security.oauth2.core.OAuth2AuthenticationException: [invalid_user_info_response] An error occurred while attempting to retrieve the UserInfo Resource: 401 null]

    通过抓包,发现,Cannot convert access token to JSON

    具体是

    请求:
    GET http://192.168.1.103:8081/auth/user/me HTTP/1.1
    Accept: application/json
    Authorization: Bearer 036685d2-ed5a-48c7-a5c7-a135a9e49468
    User-Agent: Java/1.8.0_231
    Host: 192.168.1.103:8081
    Connection: keep-alive
    
    响应:
    HTTP/1.1 401
    Access-Control-Allow-Origin: *
    Access-Control-Allow-Methods: *
    Access-Control-Max-Age: 3600
    Access-Control-Allow-Headers: x-requested-with, authorization
    Pragma: no-cache
    WWW-Authenticate: Bearer realm="oauth2-resource", error="invalid_token", error_description="Cannot convert access token to JSON"
    Cache-Control: no-store
    X-Content-Type-Options: nosniff
    X-XSS-Protection: 1; mode=block
    X-Frame-Options: DENY
    Content-Type: application/json;charset=UTF-8
    Date: Wed, 13 Jul 2022 08:02:17 GMT
    Content-Length: 83
    
    {"error":"invalid_token","error_description":"Cannot convert access token to JSON"}
    

    测试发现就是,如果把 自己注册的TokenStore 去掉就好了,为什么呢?

    @Bean  
    public TokenStore tokenStore(JwtAccessTokenConverter jwtAccessTokenConverter) {
        return new JwtTokenStore(jwtAccessTokenConverter);
    }
    

    调试半天,终于发现:

    @Configuration
    public class ResourceServerConfiguration extends WebSecurityConfigurerAdapter implements Ordered {
    
        private int order = 3;
    
        @Autowired(required = false)
        private TokenStore tokenStore;
    
        @Autowired(required = false)
        private AuthenticationEventPublisher eventPublisher;
    
        @Autowired(required = false)
        private Map<String, ResourceServerTokenServices> tokenServices;
    
        @Autowired
        private ApplicationContext context;
    
        private List<ResourceServerConfigurer> configurers = Collections.emptyList();
    
        @Autowired(required = false)
        private AuthorizationServerEndpointsConfiguration endpoints;
        
        ...
    }
    

    原来是 ResourceServerConfiguration里面有@Autowired,把我创建的JwtTokenStore注册进去了,但是呢

    调试发现其实有多个 DefaultTokenServices实例, 有的是使用InMemoryTokenStore(测试发现是 AuthorizationServer), ResourceServer使用的是JwtTokenStore

    这样就出问题了,AuthorizationServer 返回的是普通的access_token:
    {"access_token":"036685d2-ed5a-48c7-a5c7-a135a9e49468","token_type":"bearer","expires_in":42363,"scope":"user_info"}

    然后oauth2 client 使用它访问oauth2 server 的user info 端点, 然后ResourceServer使用JwtTokenStore 来解析它,尝试从其中获取 用户信息,结果就自然解析不了,报错了!

    注意:
    AuthorizationServerEndpointsConfigurer、 ResourceServerSecurityConfigurer 各有各自的 tokenStore,两者最好是相同。

    解决方法是,在配置自定义的AuthorizationServerConfigurerAdapter的时候,给endpoints的tokenStore也设置为相同jwtTokenStore, 这样就不会有转换问题了

    public class AuthServerConfig extends AuthorizationServerConfigurerAdapter {
        @Override
        public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
            endpoints
                    .accessTokenConverter(jwtAccessTokenConverter)
                    .tokenStore(jwtTokenStore) 
            ;
            super.configure(endpoints);
        }
        ...
    

    更正

    测试发现,仅仅配置.tokenStore(jwtTokenStore) 是不行的,其实只需要配置 accessTokenConverter 为jwtAccessTokenConverter即可;也就是:

    public class AuthServerConfig extends AuthorizationServerConfigurerAdapter {
        @Override
        public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
            endpoints
                    .accessTokenConverter(jwtAccessTokenConverter) 
            ;
            super.configure(endpoints);
        }
        ...
    

    why,因为accessTokenConverter 才是真正复杂发放 accessToken 的地方, 而且注意AuthorizationServerEndpointsConfigurer的tokenStore()
    tokenStore() 其实会尝试根据accessTokenConverter生成tokenStore, 如果JwtAccessTokenConverter则生成JwtTokenStore

    	private TokenStore tokenStore() {
    		if (tokenStore == null) {
    			if (accessTokenConverter() instanceof JwtAccessTokenConverter) {
    				this.tokenStore = new JwtTokenStore((JwtAccessTokenConverter) accessTokenConverter());
    			}
    			else {
    				this.tokenStore = new InMemoryTokenStore();
    			}
    		}
    		return this.tokenStore;
    	}
    

    然后就可以看到oauth/token端点返回正确的jwt格式的token了!

    请求:
    POST http://192.168.1.103:8081/auth/oauth/token HTTP/1.1
    Accept: application/json;charset=UTF-8
    Content-Type: application/x-www-form-urlencoded;charset=UTF-8
    Authorization: Basic UjJkcHhRM3ZQcnRmZ0Y3MjpmRHc3TXBrazVjekhOdVNSdG1oR21BR0w0MkNheFFCOQ==
    User-Agent: Java/1.8.0_231
    Host: 192.168.1.103:8081
    Connection: keep-alive
    Content-Length: 98
    
    grant_type=authorization_code&code=3Xfg5J&redirect_uri=http%3A%2F%2F192.168.1.103%3A8082%2FdoLogin
    
    响应: 
    {"access_token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2NTc3NDI5OTcsInVzZXJfbmFtZSI6ImEiLCJhdXRob3JpdGllcyI6WyJST0xFX1VTRVIiXSwianRpIjoiODNjNzNkYWEtMWM2OC00NTBmLWI0MDEtZWU3YjRhZWQ5ZDMyIiwiY2xpZW50X2lkIjoiUjJkcHhRM3ZQcnRmZ0Y3MiIsInNjb3BlIjpbInVzZXJfaW5mbyJdfQ.mq1tHm4DMOgIgIvQd1JcoKhqDPR79cxOLV9g3sKnv8U","token_type":"bearer","expires_in":43152,"scope":"user_info","jti":"83c73daa-1c68-450f-b401-ee7b4aed9d32"}
  • 相关阅读:
    一个tomcat 增加多个端口
    转项目经理
    apicloud 解析
    七行代码搞定 任意数据库中的数据迁移
    语语
    农家设计图纸
    日期再总结
    X删除数据表的新用法
    c#中各类日期的计算方法,收藏
    IIS 7.5 高并发参数配置
  • 原文地址:https://www.cnblogs.com/FlyAway2013/p/16482165.html
Copyright © 2020-2023  润新知