@ExceptionHandler不管用 spring security oauth2 自定义异常拦截InternalAuthenticationServiceException

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/c5113620/article/details/89576545

由于spring security的认证原理是通过注册到容器tomcat的filter链上,使得认证异常不能通过DispatcherServlet,所以@ExceptionHandler处理不到

//AuthenticationEntryPoint
{
    "error": "unauthorized",
    "error_description": "Full authentication is required to access this resource"
}
或者
//AccessDeniedHandler
{
    "error": "invalid_token",
    "error_description": "Invalid access token: 82b47091-c752-4832-b5dd-96a4da4df999"
}

{
    "error": "access_denied",
    "error_description": "not allowed access"
}

spring security oauth2主要分为两块:认证服务器与资源服务器
认证服务器,就是**/oauth/token** 登录获取认证token
资源服务器,就是管理那些url资源需要被认证和权限检查,资源服务器ResourceServerConfiguration会配置需要检查权限的url

有时候配置好认证服务器,可以获取token,但是访问资源url还是报错access_denied,试试下面配置
spring-boot-starter-parent from 1.4.2 to 1.5.2–OAuth2–access_denied

//下面的order值看OAuth2 版本
security.oauth2.resource.filter-order = 3
或者
@Order(SecurityProperties.ACCESS_OVERRIDE_ORDER) on WebSecurityConfigurerAdapter class

所以spring security oauth2的异常会出现在两处

  • 登录认证 /oauth/token 获取token时
  • 访问其他资源类url时

所以自定义异常需要配置两处

@Configuration
@EnableAuthorizationServer
public class OAuthSecurityConfig extends AuthorizationServerConfigurerAdapter {
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        //自定义 /oauth/token 异常处理
        endpoints.exceptionTranslator(new MyWebResponseExceptionTranslator());
    }
}

@Configuration
@EnableResourceServer
public  class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
        @Override
        public void configure(ResourceServerSecurityConfigurer resources) {
            //自定义资源访问认证异常,没有token,或token错误,使用MyAuthenticationEntryPoint
            resources.authenticationEntryPoint(new MyAuthenticationEntryPoint());
            resources.accessDeniedHandler(new MyAccessDeniedHandler());
        }
}
//实现更加用户名获取用户信息
@Component
public class UserDetailServiceImpl implements UserDetailsService {
    public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
        if(user==null)
            // throw OAuth2Exception.create(OAuth2Exception.INVALID_REQUEST, "无此用户名");
            throw new UsernameNotFoundException("无此用户名");
    }
}

// 默认处于安全,会把UsernameNotFoundException转为BadCredentialsException,就是 “坏的凭据”,注入下面配置的bean
@Bean
    public AuthenticationProvider daoAuthenticationProvider(UserDetailsService userDetailsService) {
        DaoAuthenticationProvider impl = new DaoAuthenticationProvider();
        impl.setUserDetailsService(userDetailsService);
        impl.setHideUserNotFoundExceptions(false) ;
        return impl ;
    }

对于登录认证异常处理配置,然后是处理类,通过WebResponseExceptionTranslator转为自定义的OAuth2Exception,然后使用jackson定制化类的json序列化,达到自定义异常信息的目的。

OAuth2Exception 本身是继承RuntimeException,而且被OAuth2ExceptionJackson1Serializer序列化,所以我们要做的就是自定义一个OAuth2Exception 子类 和这个子类的序列化

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.oauth2.common.exceptions.OAuth2Exception;
import org.springframework.security.oauth2.provider.error.WebResponseExceptionTranslator;

/**
 * @author zhanghui
 * @date 2019/6/21
 */
public class MyWebResponseExceptionTranslator implements WebResponseExceptionTranslator {
    @Override
    public ResponseEntity<OAuth2Exception> translate(Exception exception) throws Exception {
        if (exception instanceof OAuth2Exception) {
            OAuth2Exception oAuth2Exception = (OAuth2Exception) exception;
            return ResponseEntity
                    .status(oAuth2Exception.getHttpErrorCode())
                    .body(new CustomOauthException(oAuth2Exception.getMessage()));
        }else if(exception instanceof AuthenticationException){
            AuthenticationException authenticationException = (AuthenticationException) exception;
            return ResponseEntity
                    .status(HttpStatus.UNAUTHORIZED)
                    .body(new CustomOauthException(authenticationException.getMessage()));
        }
        return ResponseEntity
                .status(HttpStatus.OK)
                .body(new CustomOauthException(exception.getMessage()));
    }
}
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import org.springframework.security.oauth2.common.exceptions.OAuth2Exception;

/**
 * @author zhanghui
 * @date 2019/6/21
 */
@JsonSerialize(using = CustomOauthExceptionSerializer.class)
public class CustomOauthException extends OAuth2Exception {
    public CustomOauthException(String msg) {
        super(msg);
    }
}
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.std.StdSerializer;

import java.io.IOException;
import java.util.Arrays;
import java.util.Map;

/**
 * @author zhanghui
 * @date 2019/6/21
 */
public class CustomOauthExceptionSerializer extends StdSerializer<CustomOauthException> {

    public CustomOauthExceptionSerializer() {
        super(CustomOauthException.class);
    }

    @Override
    public void serialize(CustomOauthException value, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
        jsonGenerator.writeStartObject();
        jsonGenerator.writeNumberField("code4444", value.getHttpErrorCode());
        jsonGenerator.writeBooleanField("status", false);
        jsonGenerator.writeObjectField("data", null);
        jsonGenerator.writeObjectField("errors", Arrays.asList(value.getOAuth2ErrorCode(),value.getMessage()));
        if (value.getAdditionalInformation()!=null) {
            for (Map.Entry<String, String> entry : value.getAdditionalInformation().entrySet()) {
                String key = entry.getKey();
                String add = entry.getValue();
                jsonGenerator.writeStringField(key, add);
            }
        }
        jsonGenerator.writeEndObject();
    }
}

对于资源访问的异常自定义

import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * @author zhanghui
 * @date 2019/6/21
 */
public class MyAuthenticationEntryPoint implements AuthenticationEntryPoint {
    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response,
                         AuthenticationException authException)
            throws ServletException {

        Map map = new HashMap();
        map.put("errorentry", "401");
        map.put("message", authException.getMessage());
        map.put("path", request.getServletPath());
        map.put("timestamp", String.valueOf(new Date().getTime()));
        response.setContentType("application/json");
        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
        try {
            ObjectMapper mapper = new ObjectMapper();
            mapper.writeValue(response.getOutputStream(), map);
        } catch (Exception e) {
            throw new ServletException();
        }
    }

}
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * @author zhanghui
 * @date 2019/6/21
 */
public class MyAccessDeniedHandler implements AccessDeniedHandler{
    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
        response.setContentType("application/json;charset=UTF-8");
        Map map = new HashMap();
        map.put("errorauth", "400");
        map.put("message", accessDeniedException.getMessage());
        map.put("path", request.getServletPath());
        map.put("timestamp", String.valueOf(new Date().getTime()));
        response.setContentType("application/json");
        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
        try {
            ObjectMapper mapper = new ObjectMapper();
            mapper.writeValue(response.getOutputStream(), map);
        } catch (Exception e) {
            throw new ServletException();
        }
    }
}

猜你喜欢

转载自blog.csdn.net/c5113620/article/details/89576545