使用SpringSecurity+Oauth2作為安全框架,無效token調用/oauth/check_token時返回500錯誤的解決方案


項目安全框架:SpringSecurity+Oauth2+JWZ,采用集中認證授權方式。配置不在這里具體寫了,可參考百度或google的通用配置。

然而,在項目中當使用無效的access_token或者不帶token進行資源訪問時,我發現前端出現了【500 內部錯誤】的信息,並沒有如預期那樣出現【401Unauthorized】的狀態碼。

調查日志發現如下報錯:

21:12:55.345 [http-nio-9201-exec-1] ERROR o.a.c.c.C.[.[localhost] - [log,175] - Exception Processing ErrorPage[errorCode=0, location=/error]
org.springframework.web.client.HttpClientErrorException$Unauthorized: 401 : [no body]
    at org.springframework.web.client.HttpClientErrorException.create(HttpClientErrorException.java:105)
    at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:170)
    at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:112)
    at org.springframework.web.client.ResponseErrorHandler.handleError(ResponseErrorHandler.java:63)
    at org.springframework.web.client.RestTemplate.handleResponse(RestTemplate.java:782)
    at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:740)
    at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:674)
    at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:583)
    at org.springframework.security.oauth2.provider.token.RemoteTokenServices.postForMap(RemoteTokenServices.java:149)
    at org.springframework.security.oauth2.provider.token.RemoteTokenServices.loadAuthentication(RemoteTokenServices.java:106)
    at org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationManager.authenticate(OAuth2AuthenticationManager.java:83)
    at org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationProcessingFilter.doFilter(OAuth2AuthenticationProcessingFilter.java:150)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
    at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:116)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:103)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
    at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:105)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:103)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:334)
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:215)
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:178)
    at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:358)
    at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:271)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
    at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:712)
    at org.apache.catalina.core.ApplicationDispatcher.processRequest(ApplicationDispatcher.java:461)
    at org.apache.catalina.core.ApplicationDispatcher.doForward(ApplicationDispatcher.java:384)
    at org.apache.catalina.core.ApplicationDispatcher.forward(ApplicationDispatcher.java:312)
    at org.apache.catalina.core.StandardHostValve.custom(StandardHostValve.java:394)
    at org.apache.catalina.core.StandardHostValve.status(StandardHostValve.java:253)
    at org.apache.catalina.core.StandardHostValve.throwable(StandardHostValve.java:348)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:173)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)
    at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:373)
    at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
    at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:868)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1594)
    at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Thread.java:748)
View Code
這部分內容是最重要的內容,也是解決問題的思路來源,我截取出來:

21:12:55.345 [http-nio-9201-exec-1] ERROR o.a.c.c.C.[.[localhost] - [log,175] - Exception Processing ErrorPage[errorCode=0, location=/error] org.springframework.web.client.HttpClientErrorException$Unauthorized: 401 : [no body] at org.springframework.web.client.HttpClientErrorException.create(HttpClientErrorException.java:105) at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:170) at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:112) at org.springframework.web.client.ResponseErrorHandler.handleError(ResponseErrorHandler.java:63) at org.springframework.web.client.RestTemplate.handleResponse(RestTemplate.java:782) at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:740) at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:674) at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:583) at

 

從日志可以看出,當進行token有效檢查的時候,會通過httpclient調用/oauth/check_token這個api進行認證,token無效的時候,其實已經返回了401的狀態碼,

但是我們根據錯誤日志(上圖的紅色文字部分),通過查看RestTemplate.class的源碼可發現

    protected void handleResponse(URI url, HttpMethod method, ClientHttpResponse response) throws IOException {
        ResponseErrorHandler errorHandler = this.getErrorHandler();
        boolean hasError = errorHandler.hasError(response);
        if(this.logger.isDebugEnabled()) {
            try {
                int code = response.getRawStatusCode();
                HttpStatus status = HttpStatus.resolve(code);
                this.logger.debug("Response " + (status != null?status:Integer.valueOf(code)));
            } catch (IOException var8) {
                ;
            }
        }

        if(hasError) { errorHandler.handleError(url, method, response); }

    }

 

當check_token發生錯誤時,這里直接進行了異常處理,未對返回的信息做解析,因此我們得到了這樣的錯誤信息“org.springframework.web.client.HttpClientErrorException$Unauthorized: 401 : [no body]”。

解決方法也很簡單,根據錯誤日志的提示,我們重寫自己的ResponseErrorHandler就行。比如:

import org.springframework.http.client.ClientHttpResponse;
import org.springframework.web.client.ResponseErrorHandler;
import java.io.IOException;

/**
 * @Description: 定制無效token時的錯誤信息處理
 * @Author : yyq
 */
public class CustomerResponseErrorHandler implements ResponseErrorHandler {

    @Override
    public boolean hasError(ClientHttpResponse response) throws IOException {
        // 這里返回false
        return false;
    }

    @Override
    public void handleError(ClientHttpResponse response) throws IOException {

    }

}

 

然后在ResourceServerConfig文件中加入我們自定義的ErrorHandler配置

    @Bean
    @LoadBalanced
    public RestTemplate restTemplate()
    {
        RestTemplate restTemplate = new RestTemplate();
        restTemplate.setErrorHandler(new TkResponseErrorHandler());
        return restTemplate;
    }

 

PS:如果有同學出現的錯誤信息是返回的400 BadRequest,可能是AuthorizationServerConfigurerAdapter配置中少配置了自定義異常的選項。可參考

    /**
     * 定義授權和令牌端點以及令牌服務
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints)
    {
        endpoints
                // 請求方式
                .allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST)
                // 指定token存儲位置
                .tokenStore(tokenStore())
                // 自定義生成令牌
//                .tokenEnhancer(tokenEnhancer)
                .tokenEnhancer(tokenEnhancer())
                // 用戶賬號密碼認證
                .userDetailsService(userDetailsService)
                // 指定認證管理器
                .authenticationManager(authenticationManager)
                // 自定義異常處理
                .exceptionTranslator(new CustomWebResponseExceptionTranslator());
。。。

 

至此,以上問題應該可以解決。如果有出現其他問題的同學,可以留言溝通!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM