SpringBoot Security的自定义异常处理

SpringBoot Security自定义异常

access_denied 方面异常

原异常

{

"error": "access_denied",

"error_description": "不允许访问"

}

现异常

{

"success": false,

"error": "access_denied",

"status": 403,

"message": "不允许访问",

"path": "/user/get1",

"timestamp": 1592378892768

}

实现

public class ResourceServerConfig extends ResourceServerConfigurerAdapter {

@Override

public void configure(ResourceServerSecurityConfigurer resources) {

// access_denied 方面异常

OAuth2AccessDeniedHandler oAuth2AccessDeniedHandler = new OAuth2AccessDeniedHandler();

oAuth2AccessDeniedHandler.setExceptionTranslator(new CustomWebResponseExceptionTranslator());

resources.accessDeniedHandler(oAuth2AccessDeniedHandler);

}

}

Invalid access token 方面异常

原异常

{

"error": "invalid_token",

"error_description": "Invalid access token: 4eb58ecf-e66de-4155-9477-64a1c9805cc8"

}

现异常

{

"success": false,

"error": "invalid_token",

"status": 401,

"message": "Invalid access token: 8cd45925dbf6-4502-bd13-8101bc6e1d4b",

"path": "/user/get1",

"timestamp": 1592378949452

}

实现

public class ResourceServerConfig extends ResourceServerConfigurerAdapter {

@Override

public void configure(ResourceServerSecurityConfigurer resources) {

// Invalid access token 方面异常

OAuth2AuthenticationEntryPoint authenticationEntryPoint = new OAuth2AuthenticationEntryPoint();

authenticationEntryPoint.setExceptionTranslator(new CustomWebResponseExceptionTranslator());

resources.authenticationEntryPoint(authenticationEntryPoint);

}

}

Bad credentials 方面异常(登陆出错)

原异常

{

"error": "invalid_grant",

"error_description": "用户名或密码错误"

}

现异常

{

"success": false,

"error": "invalid_grant",

"status": 400,

"message": "用户名或密码错误",

"path": "/oauth/token",

"timestamp": 1592384576019

}

实现

public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

@Override

public void configure(AuthorizationServerEndpointsConfigurer endpoints) {

endpoints.userDetailsService(detailsService)

.tokenStore(memoryTokenStore())

.exceptionTranslator(new CustomWebResponseExceptionTranslator())

.authenticationManager(authenticationManager)

//接收GET和POST

.allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST);

}

}

其他类

@Getter

@JsonSerialize(using = CustomOauthExceptionSerializer.class)

public class CustomOauthException extends OAuth2Exception {

private String oAuth2ErrorCode;

private int httpErrorCode;

public CustomOauthException(String msg, String oAuth2ErrorCode, int httpErrorCode) {

super(msg);

this.oAuth2ErrorCode = oAuth2ErrorCode;

this.httpErrorCode = httpErrorCode;

}

}

public class CustomOauthExceptionSerializer extends StdSerializer<CustomOauthException> {

private static final long serialVersionUID = 2652127645704345563L;

public CustomOauthExceptionSerializer() {

super(CustomOauthException.class);

}

@Override

public void serialize(CustomOauthException value, JsonGenerator gen, SerializerProvider provider) throws IOException {

gen.writeStartObject();

HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();

gen.writeObjectField("success",false);

gen.writeObjectField("error",value.getOAuth2ErrorCode());

gen.writeObjectField("status", value.getHttpErrorCode());

gen.writeObjectField("message", value.getMessage());

gen.writeObjectField("path", request.getServletPath());

gen.writeObjectField("timestamp", (new Date()).getTime());

if (value.getAdditionalInformation()!=null) {

for (Map.Entry<String, String> entry : value.getAdditionalInformation().entrySet()) {

String key = entry.getKey();

String add = entry.getValue();

gen.writeObjectField(key, add);

}

}

gen.writeEndObject();

}

}

public class CustomWebResponseExceptionTranslator extends DefaultWebResponseExceptionTranslator {

@Override

public ResponseEntity<OAuth2Exception> translate(Exception e) throws Exception {

ResponseEntity<OAuth2Exception> translate = super.translate(e);

OAuth2Exception body = translate.getBody();

CustomOauthException customOauthException = new CustomOauthException(body.getMessage(),body.getOAuth2ErrorCode(),body.getHttpErrorCode());

ResponseEntity<OAuth2Exception> response = new ResponseEntity<>(customOauthException, translate.getHeaders(),

translate.getStatusCode());

return response;

}

}

补充

{

"error": "invalid_client",

"error_description": "Bad client credentials"

}

如果client_secret错误依然还是报错,如上内容,针对这个异常需要在如下方法中的addTokenEndpointAuthenticationFilter添加过滤器处理

@Override

public void configure(AuthorizationServerSecurityConfigurer oauthServer) {

oauthServer

// 开启/oauth/token_key验证端口无权限访问

.tokenKeyAccess("permitAll()")

// 开启/oauth/check_token验证端口认证权限访问

.checkTokenAccess("isAuthenticated()")

.addTokenEndpointAuthenticationFilter(null)

.allowFormAuthenticationForClients();

}

SpringSecurity自定义响应异常信息

此处的异常信息设置的话,其中还是有坑的,比如你想自定义token过期信息,无效token这些,如果按照SpringSecurity的设置是不会生效的,需要加到资源的配置中。

如果只是SpringSecurity的话,只需要实现AccessDeniedHandler和AuthenticationEntryPoint这2个接口就可以了。他们都是在ExceptionTranslationFilter中生效的。

  • AuthenticationEntryPoint 用来解决匿名用户访问无权限资源时的异常
  • ruAccessDeineHandler 用来解决认证过的用户访问无权限资源时的异常

image-20210823192313538

如果你想自定义token过期的话,需要实现AuthenticationEntryPoint这个接口,因为token过期了,访问的话也算是匿名访问。

但是SpringSecurity的过滤器链中其实是有顺序的,校验token的OAuth2AuthenticationProcessingFilter在它前面,导致一直没有办法生效,所有需要添加到资源的配置上,demo如下:

/**

* @author WGR

* @create 2021/8/23 -- 16:52

*/

@Component

public class SimpleAuthenticationEntryPoint implements AuthenticationEntryPoint {

@Override

public void commence(HttpServletRequest request, HttpServletResponse response,

AuthenticationException authException) throws ServletException {

Throwable cause = authException.getCause();

try {

if (cause instanceof InvalidTokenException) {

Map map = new HashMap();

map.put("error", "无效token");

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();

}

}

} catch (Exception e) {

e.printStackTrace();

}

}

}

image-20210823192757483

则可以生效,返回信息具体如下:

image-20210823192828621

如果想设置没有权限的自定义异常信息的话:

/**

* @author WGR

* @create 2021/8/23 -- 17:09

*/

@Component

public class SimpleAccessDeniedHandler implements AccessDeniedHandler {

@Override

public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {

Map map = new HashMap();

map.put("message", "无权操作");

map.put("path", request.getServletPath());

map.put("timestamp", String.valueOf(new Date().getTime()));

response.setContentType("application/json");

response.setStatus(HttpServletResponse.SC_FORBIDDEN);

try {

ObjectMapper mapper = new ObjectMapper();

mapper.writeValue(response.getOutputStream(), map);

} catch (Exception e) {

throw new ServletException();

}

}

}

把它设置到springsecurity中,添加进去就可以了,如果不是想要捕获token过期的话,就直接添加进去也可以

image-20210823194105642

image-20210823193825241

以上为个人经验,希望能给大家一个参考,也希望大家多多支持。

以上是 SpringBoot Security的自定义异常处理 的全部内容, 来源链接: utcz.com/p/251659.html

回到顶部