《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》

摘要: 原创出处 http://niocoder.com/2018/05/27/Spring-Security-Oauth2-自定义token-Exception/ 「龙飞」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注**微信公众号:【芋道源码】**有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言将得到认真回复。甚至不知道如何读源码也可以请教噢
  4. 新的源码解析文章实时收到通知。每周更新一篇左右
  5. 认真的源码交流微信群。

付出就要得到回报,这种想法是错的。

https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/spring-security-OAuth208.png

1. 前言

在使用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"
}

1.1 自定义登录失败异常信息

1.1.1 新增CustomOauthException

  • 添加自定义异常类,指定json序列化方式

@JsonSerialize(using = CustomOauthExceptionSerializer.class)
public class CustomOauthException extends OAuth2Exception {
public CustomOauthException(String msg) {
super(msg);
}
}

1.1.2 新增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();
}
}

1.1.3 添加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();
}
}

1.1.4 修改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);
}

1.2 自定义Token异常信息

1.2.1 添加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();
}
}
}

1.2.2 添加CustomAccessDeniedHandler

  • 授权失败(forbidden)时返回信息

@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));
}
}

1.2.3 修改MerryyouResourceServerConfig

@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.authenticationEntryPoint(new AuthExceptionEntryPoint())
.accessDeniedHandler(CustomAccessDeniedHandler);
}

1.3 效果如下

1.3.1 登录异常

https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/security/spring-security-oauth209.gif

1.3.2 token异常

https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/security/spring-security-oauth210.gif

1.3.3 禁止访问

https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/security/spring-security-oauth211.gif

1.3.4 token失效

https://raw.githubusercontent.com/longfeizheng/longfeizheng.github.io/master/images/security/spring-security-oauth212.gif

2. 代码下载

3. 推荐文章

  1. Java创建区块链系列
  2. Spring Security源码分析系列
  3. Spring Data Jpa 系列
  4. 【译】数据结构中关于树的一切(java版)
  5. SpringBoot+Docker+Git+Jenkins实现简易的持续集成和持续部署
文章目录
  1. 1. 1. 前言
    1. 1.1. 1.1 自定义登录失败异常信息
      1. 1.1.1. 1.1.1 新增CustomOauthException
      2. 1.1.2. 1.1.2 新增CustomOauthExceptionSerializer
      3. 1.1.3. 1.1.3 添加CustomWebResponseExceptionTranslator
      4. 1.1.4. 1.1.4 修改MerryyouAuthorizationServerConfig
    2. 1.2. 1.2 自定义Token异常信息
      1. 1.2.1. 1.2.1 添加AuthExceptionEntryPoint
      2. 1.2.2. 1.2.2 添加CustomAccessDeniedHandler
      3. 1.2.3. 1.2.3 修改MerryyouResourceServerConfig
    3. 1.3. 1.3 效果如下
      1. 1.3.1. 1.3.1 登录异常
      2. 1.3.2. 1.3.2 token异常
      3. 1.3.3. 1.3.3 禁止访问
      4. 1.3.4. 1.3.4 token失效
  2. 2. 2. 代码下载
  3. 3. 3. 推荐文章