SpringBoot?Security的自定義異常處理
SpringBoot Security自定義異常
access_denied 方面異常
原異常
{
"error": "access_denied",
"error_description": "不允許訪問"
}
現(xiàn)異常
{
"success": false,
"error": "access_denied",
"status": 403,
"message": "不允許訪問",
"path": "/user/get1",
"timestamp": 1592378892768
}
實現(xiàn)
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"
}
現(xiàn)異常
{
"success": false,
"error": "invalid_token",
"status": 401,
"message": "Invalid access token: 8cd45925dbf6-4502-bd13-8101bc6e1d4b",
"path": "/user/get1",
"timestamp": 1592378949452
}
實現(xiàn)
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": "用戶名或密碼錯誤"
}
現(xiàn)異常
{
"success": false,
"error": "invalid_grant",
"status": 400,
"message": "用戶名或密碼錯誤",
"path": "/oauth/token",
"timestamp": 1592384576019
}
實現(xiàn)
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錯誤依然還是報錯,如上內(nèi)容,針對這個異常需要在如下方法中的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的話,只需要實現(xiàn)AccessDeniedHandler和AuthenticationEntryPoint這2個接口就可以了。他們都是在ExceptionTranslationFilter中生效的。
AuthenticationEntryPoint用來解決匿名用戶訪問無權限資源時的異常ruAccessDeineHandler用來解決認證過的用戶訪問無權限資源時的異常

如果你想自定義token過期的話,需要實現(xiàn)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();
}
}
}

則可以生效,返回信息具體如下:

如果想設置沒有權限的自定義異常信息的話:
/**
* @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過期的話,就直接添加進去也可以


以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
使用自定義參數(shù)解析器同一個參數(shù)支持多種Content-Type
這篇文章主要介紹了使用自定義參數(shù)解析器同一個參數(shù)支持多種Content-Type的操作,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-08-08
Spring Data JPA 如何使用QueryDsl查詢并分頁
這篇文章主要介紹了Spring Data JPA 如何使用QueryDsl查詢并分頁,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-11-11
Java利用策略模式實現(xiàn)條件判斷,告別if else
策略模式定義了一系列算法,并且將每個算法封裝起來,使得他們可以相互替換,而且算法的變化不會影響使用算法的客戶端。本文將通過案例講解如何利用Java的策略模式實現(xiàn)條件判斷,告別if----else條件硬編碼,需要的可以參考一下2022-02-02

