付出就要得到回报,这种想法是错的。
前言
在使用Spring Security Oauth2
登录和鉴权失败时,默认返回的异常信息如下
{ "error": "unauthorized", "error_description": "Full authentication is required to access this resource" }
。它与我们自定义返回信息不一致,并且描述信息较少。那么如何自定义Spring Security Oauth2
异常信息呢,下面我们简单实现以下。格式如下:
{ "error": "400", "message": "坏的凭证", "path": "/oauth/token", "timestamp": "1527432468717" }
自定义登录失败异常信息
新增CustomOauthException
@JsonSerialize(using = CustomOauthExceptionSerializer.class) public class CustomOauthException extends OAuth2Exception { public CustomOauthException(String msg) { super(msg); } }
新增CustomOauthExceptionSerializer
- 添加
CustomOauthException
的序列化实现
public class CustomOauthExceptionSerializer extends StdSerializer<CustomOauthException> { public CustomOauthExceptionSerializer() { super(CustomOauthException.class); } @Override public void serialize(CustomOauthException value, JsonGenerator gen, SerializerProvider provider) throws IOException { HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); gen.writeStartObject(); gen.writeStringField("error", String.valueOf(value.getHttpErrorCode())); gen.writeStringField("message", value.getMessage()); // gen.writeStringField("message", "用户名或密码错误"); gen.writeStringField("path", request.getServletPath()); gen.writeStringField("timestamp", String.valueOf(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.writeStringField(key, add); } } gen.writeEndObject(); } }
添加CustomWebResponseExceptionTranslator
- 添加
CustomWebResponseExceptionTranslator
,登录发生异常时指定exceptionTranslator
public class CustomOauthExceptionSerializer extends StdSerializer<CustomOauthException> { public CustomOauthExceptionSerializer() { super(CustomOauthException.class); } @Override public void serialize(CustomOauthException value, JsonGenerator gen, SerializerProvider provider) throws IOException { HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); gen.writeStartObject(); gen.writeStringField("error", String.valueOf(value.getHttpErrorCode())); gen.writeStringField("message", value.getMessage()); // gen.writeStringField("message", "用户名或密码错误"); gen.writeStringField("path", request.getServletPath()); gen.writeStringField("timestamp", String.valueOf(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.writeStringField(key, add); } } gen.writeEndObject(); } }
修改MerryyouAuthorizationServerConfig
- 指定自定义
customWebResponseExceptionTranslator
@Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { endpoints.tokenStore(tokenStore) .authenticationManager(authenticationManager) .userDetailsService(userDetailsService); //扩展token返回结果 if (jwtAccessTokenConverter != null && jwtTokenEnhancer != null) { TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain(); List<TokenEnhancer> enhancerList = new ArrayList(); enhancerList.add(jwtTokenEnhancer); enhancerList.add(jwtAccessTokenConverter); tokenEnhancerChain.setTokenEnhancers(enhancerList); //jwt endpoints.tokenEnhancer(tokenEnhancerChain) .accessTokenConverter(jwtAccessTokenConverter); } endpoints.exceptionTranslator(customWebResponseExceptionTranslator); }
自定义Token异常信息
添加AuthExceptionEntryPoint
- 自定义
AuthExceptionEntryPoint
用于tokan
校验失败返回信息
public class AuthExceptionEntryPoint implements AuthenticationEntryPoint { @Override public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws ServletException { Map map = new HashMap(); map.put("error", "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(); } } }
添加CustomAccessDeniedHandler
@Slf4j @Component("customAccessDeniedHandler") public class CustomAccessDeniedHandler implements AccessDeniedHandler { @Autowired private ObjectMapper objectMapper; @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("error", "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); response.getWriter().write(objectMapper.writeValueAsString(map)); } }
修改MerryyouResourceServerConfig
@Override public void configure(ResourceServerSecurityConfigurer resources) throws Exception { resources.authenticationEntryPoint(new AuthExceptionEntryPoint()) .accessDeniedHandler(CustomAccessDeniedHandler); }
效果如下
登录异常
token异常
禁止访问
token失效
代码下载
推荐文章
- Java创建区块链系列
- Spring Security源码分析系列
- Spring Data Jpa 系列
- 【译】数据结构中关于树的一切(java版)
- SpringBoot+Docker+Git+Jenkins实现简易的持续集成和持续部署