Spring Cloud实战 | 第九篇:Spring Cloud整合Spring Security OAuth2认证服务器统一认证自定义异常处理

简介: Spring Cloud实战 | 第九篇:Spring Cloud整合Spring Security OAuth2认证服务器统一认证自定义异常处理

本文完整代码下载点击

微信图片_20230709224442.gif

一. 前言

相信了解过我或者看过我之前的系列文章应该多少知道点我写这些文章包括创建 有来商城youlai-mall 这个项目的目的,想给那些真的想提升自己或者迷茫的人(包括自己–一个工作6年觉得一无是处的菜鸟)提供一块上升的基石。项目是真的从无到有(往期文章佐证),且使用当前主流的开发模式(微服务+前后端分离),最新主流的技术栈(Spring Boot+ Spring Cloud +Spring Cloud Alibaba + Vue),最流行的统一安全认证授权(OAuth2+JWT),好了玩笑开完了大家别当真,总之有兴趣一起的小伙伴欢迎加入~


接下来说下这篇文章的原因,之前我是没想过应用到项目中的OAuth2+JWT这套组合拳这么受大家关注,期间一直有童鞋问怎么自定义Spring Security OAuth2的异常处理、JWT怎么续期、JWT退出等场景下如何失效等问题,所以最近有点时间想把这套统一认证授权完善掉,本篇就以如何自定义Spring Security OAuth2异常处理展开。


往期文章链接:


后端


Spring Cloud实战 | 第一篇:Windows搭建Nacos服务

Spring Cloud实战 | 第二篇:Spring Cloud整合Nacos实现注册中心

Spring Cloud实战 | 第三篇:Spring Cloud整合Nacos实现配置中心

Spring Cloud实战 | 第四篇:Spring Cloud整合Gateway实现API网关

Spring Cloud实战 | 第五篇:Spring Cloud整合OpenFeign实现微服务之间的调用

Spring Cloud实战 | 第六篇:Spring Cloud Gateway+Spring Security OAuth2+JWT实现微服务统一认证授权

Spring Cloud实战 | 最七篇:Spring Cloud Gateway+Spring Security OAuth2集成统一认证授权平台下实现注销使JWT失效方案

Spring Cloud实战 | 最八篇:Spring Cloud +Spring Security OAuth2+ Vue前后端分离模式下无感知刷新实现JWT续期

Spring Cloud实战 | 最九篇:Spring Security OAuth2认证服务器统一认证自定义异常处理

管理前端


vue-element-admin实战 | 第一篇: 移除mock接入后台,搭建有来商城youlai-mall前后端分离管理平台

vue-element-admin实战 | 第二篇: 最小改动接入后台实现根据权限动态加载菜单

微信小程序


vue+uniapp商城实战 | 第一篇:【有来小店】微信小程序快速开发接入Spring Cloud OAuth2认证中心完成授权登录

二. 自定义异常实现代码

直接需要答案的本节走起,添加和修改三个文件即可,异常分析,点击下载完整工程代码


1. 在youlai-auth认证服务器模块添加全局异常处理器AuthExceptionHandler


package com.youlai.auth.exception;


import com.youlai.common.core.result.Result;

import com.youlai.common.core.result.ResultCode;

import lombok.extern.slf4j.Slf4j;

import org.springframework.security.authentication.InternalAuthenticationServiceException;

import org.springframework.security.oauth2.common.exceptions.InvalidGrantException;

import org.springframework.web.bind.annotation.ExceptionHandler;

import org.springframework.web.bind.annotation.RestControllerAdvice;


@RestControllerAdvice

@Slf4j

public class AuthExceptionHandler {

   /**

    * 用户名和密码错误

    *

    * @param e

    * @return

    */

   @ExceptionHandler(InvalidGrantException.class)

   public Result handleInvalidGrantException(InvalidGrantException e) {

       return Result.custom(ResultCode.USERNAME_OR_PASSWORD_ERROR);

   }

   /**

    * 账户异常(禁用、锁定、过期)

    *

    * @param e

    * @return

    */

   @ExceptionHandler({InternalAuthenticationServiceException.class})

   public Result handleInternalAuthenticationServiceException(InternalAuthenticationServiceException e) {

       return Result.error(e.getMessage());

   }

}


2. 重写ClientCredentialsTokenEndpointFilter实现客户端自定义异常处理


package com.youlai.auth.filter;

import org.springframework.security.authentication.AuthenticationManager;

import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;

import org.springframework.security.oauth2.provider.client.ClientCredentialsTokenEndpointFilter;

import org.springframework.security.web.AuthenticationEntryPoint;

/**

* 重写filter实现客户端自定义异常处理

*/

public class CustomClientCredentialsTokenEndpointFilter extends ClientCredentialsTokenEndpointFilter {

   private AuthorizationServerSecurityConfigurer configurer;

   private AuthenticationEntryPoint authenticationEntryPoint;

   public CustomClientCredentialsTokenEndpointFilter(AuthorizationServerSecurityConfigurer configurer) {

       this.configurer = configurer;

   }

   @Override

   public void setAuthenticationEntryPoint(AuthenticationEntryPoint authenticationEntryPoint) {

       super.setAuthenticationEntryPoint(null);

       this.authenticationEntryPoint = authenticationEntryPoint;

   }

   @Override

   protected AuthenticationManager getAuthenticationManager() {

       return configurer.and().getSharedObject(AuthenticationManager.class);

   }

   @Override

   public void afterPropertiesSet() {

       setAuthenticationFailureHandler((request, response, e) -> authenticationEntryPoint.commence(request, response, e));

       setAuthenticationSuccessHandler((request, response, authentication) -> {

       });

   }

}



3. AuthorizationServerConfig认证服务器配置修改


/**

* 授权服务配置

*/

@Configuration

@EnableAuthorizationServer

@AllArgsConstructor

public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {


   ......


   @Override

   public void configure(AuthorizationServerSecurityConfigurer security) {

       /*security.allowFormAuthenticationForClients();*/

       CustomClientCredentialsTokenEndpointFilter endpointFilter = new CustomClientCredentialsTokenEndpointFilter(security);

       endpointFilter.afterPropertiesSet();

       endpointFilter.setAuthenticationEntryPoint(authenticationEntryPoint());

       security.addTokenEndpointAuthenticationFilter(endpointFilter);

       security.authenticationEntryPoint(authenticationEntryPoint())

               .tokenKeyAccess("isAuthenticated()")

               .checkTokenAccess("permitAll()");

   }

   @Bean

   public AuthenticationEntryPoint authenticationEntryPoint() {

       return (request, response, e) -> {

           response.setStatus(HttpStatus.HTTP_OK);

           response.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_UTF8_VALUE);

           response.setHeader("Access-Control-Allow-Origin", "*");

           response.setHeader("Cache-Control", "no-cache");

           Result result = Result.custom(ResultCode.CLIENT_AUTHENTICATION_FAILED);

           response.getWriter().print(JSONUtil.toJsonStr(result));

           response.getWriter().flush();

       };

   }

   ......

}


三. 异常处理分析

其实你搜一下有关Spring Security OAuth2如何自定义异常处理,网上会很多差不多解决方案提供参考,但是照搬过来试用,一点效果没有?!咋回事么?其实不能武断的说人家方案不行,最多的可能是Spring Security OAuth2版本不一致,本篇项目使用的是2.3.4版本,目前截止写这篇文章最新一版的是2020.5.28发布的2.5.0版本,后续项目会升级,如果有差异我会修改本篇文章,总之给大家提供一个解决思路,可行不可行我是不希望大家不能在我里浪费时间。


好了正文开始了~ Spring Security OAuth2认证服务器异常目前我知道的有3类:


用户名或密码错误

账户状态异常

客户端认证异常

有知道其他的欢迎留言补充~,以下就这3类异常逐一分析


在异常处理之前先看下UserDetailsServiceImpl#loadUserByUsername方法抛出的异常信息,如下图:


微信图片_20230709224513.png


1. 用户名或密码错误

异常分析

org.springframework.security.oauth2.common.exceptions.InvalidGrantException: 用户名或密码错误

1微信图片_20230709224524.png



通过异常堆栈信息定位到最终抛出异常的方法是ResourceOwnerPasswordTokenGranter#getOAuth2Authentication,异常类型是InvalidGrantException,其实到这个异常类型中间经过几道转换UsernameNotFoundException->BadCredentialsException->InvalidGrantException


处理方法

添加全局异常处理器捕获(定位标识:AuthExceptionHandler)



/**

* 用户名和密码异常

*

* @param e

* @return

*/

@ExceptionHandler(InvalidGrantException.class)

public Result handleInvalidGrantException(InvalidGrantException e) {

   return Result.error(ResultCode.USERNAME_OR_PASSWORD_ERROR);

}


结果验证

验证成功,已按照自定义异常格式返回

微信图片_20230709224527.png



2. 账户状态异常

异常分析

首先我们需要把数据库youlai的表sys_user的字段status设置为0,表示不可用状态,然后输入正确的用户名和密码,看看跑出来的原生异常信息,可惜的是这个异常没有打印堆栈信息,不过没关系,我们断点调试下,最终定位到ProviderManager#authenticate方法抛出的异常,异常类型是InternalAuthenticationServiceException。

微信图片_20230709224546.png



处理方法

添加全局异常处理器捕获(定位标识:AuthExceptionHandler)


/**

* 账户异常(禁用、锁定、过期)

*

* @param e

* @return

*/

@ExceptionHandler({InternalAuthenticationServiceException.class})

public Result handleInternalAuthenticationServiceException(InternalAuthenticationServiceException e) {

   return Result.error(e.getMessage());

}


结果验证

验证成功,已按照自定义异常格式返回

微信图片_20230709224550.png



3. 客户端认证异常

异常分析

之前两种异常方式都可以通过全局异常处理器捕获,且@RestControllerAdvice只能捕获Controller的异常。


客户端认证的异常则是发生在过滤器filter上,此时还没进入DispatcherServlet请求处理流程,便无法通过全局异常处理器捕获。


先看下客户端认证异常出现的位置,首先把客户端ID改成错的。


微信图片_20230709224610.png


然后执行“登录”操作,返回错误信息如下:


{"error":"invalid_client","error_description":"Bad client credentials"}

1

一眼望去,这显然不是我们想要的格式。


那怎么做才能捕获这个异常转换成自定义数据格式返回呢?显然全局异常处理器无法实现,那必须转换下思路了。


首先客户端的认证是交由ClientCredentialsTokenEndpointFilter来完成的,其中有后置添加失败处理方法,最后把异常交给OAuth2AuthenticationEntryPoint这个所谓认证入口处理。


微信图片_20230709224647.png


认证入口OAuth2AuthenticationEntryPoint#commence方法中转给父类AbstractOAuth2SecurityExceptionHandler#doHandle方法。


微信图片_20230709224650.png


最后异常定格在AbstractOAuth2SecurityExceptionHandler#doHandle方法上,如下图:

微信图片_20230709224708.png微信图片_20230709224710.png



其中this.enhanceResponse是调用OAuth2AuthenticationEntryPoint#enhanceResponse方法得到响应结果数据。


处理方法

上面我们得知客户端的认证失败异常是过滤器ClientCredentialsTokenEndpointFilter转交给OAuth2AuthenticationEntryPoint得到响应结果的,既然这样我们就可以重写ClientCredentialsTokenEndpointFilter然后使用自定义的AuthenticationEntryPoint替换原生的OAuth2AuthenticationEntryPoint,在自定义AuthenticationEntryPoint处理得到我们想要的异常数据。


自定义AuthenticationEntryPoint设置异常响应数据格式


微信图片_20230709224725.png


重写ClientCredentialsTokenEndpointFilter替换AuthenticationEntryPoint

微信图片_20230709224728.png



认证服务器配置添加自定义过滤器

微信图片_20230709224743.png



结果验证

验证成功,已按照自定义异常格式返回


微信图片_20230709224745.png


四. 总结

至此,认证服务器的自定义异常处理已全部处理完毕,资源服务器异常处理说明在这篇文章 Spring Cloud实战 | 第六篇:Spring Cloud Gateway+Spring Security OAuth2+JWT实现微服务统一认证授权,这就宣告 youlai-mall 的统一认证授权模块基本达到完善的一个标准, 后面继续回到业务功能的开发,所以觉得对你有帮助的给个关注(持续更新)或者给个star,灰常感谢! 最重要的如果你真的对这个项目有兴趣想一起开发学习的像文章开始说的那样请联系我哈~


相关文章
|
13天前
|
存储 监控 调度
云服务器成本优化深度解析与实战案例
本文深入探讨了云服务器成本优化的策略与实践,涵盖基本原则、具体策略及案例分析。基本原则包括以实际需求为导向、动态调整资源、成本控制为核心。具体策略涉及选择合适计费模式、优化资源配置、存储与网络配置、实施资源监控与审计、应用性能优化、利用优惠政策及考虑多云策略。文章还通过电商、制造企业和初创团队的实际案例,展示了云服务器成本优化的有效性,最后展望了未来的发展趋势,包括智能化优化、多云管理和绿色节能。
|
2月前
|
机器学习/深度学习 弹性计算 运维
云计算系列之阿里云ECS服务器管理实战
本文档介绍了阿里云ECS(Elastic Compute Service)的基本概念、实例管理、磁盘操作、快照与镜像功能及其应用场景,最后通过具体案例解析ECS的实际应用。ECS是阿里云提供的高效、可靠的云计算服务,支持多种业务需求,如Web应用、高并发网站、数据库等,帮助企业快速构建稳定安全的应用,提升运维效率,降低IT成本。文档还详细说明了ECS实例的创建方式、连接方法及日常管理操作,帮助用户更好地利用ECS服务。
82 2
云计算系列之阿里云ECS服务器管理实战
|
3月前
|
网络协议
keepalived对后端服务器的监测方式实战案例
关于使用keepalived进行后端服务器TCP监测的实战案例,包括配置文件的编辑和keepalived服务的重启,以确保配置生效。
80 1
keepalived对后端服务器的监测方式实战案例
|
4月前
|
SQL 缓存 自然语言处理
实战案例1:基于C语言的Web服务器实现。
实战案例1:基于C语言的Web服务器实现。
232 15
|
4月前
|
运维 数据安全/隐私保护 数据库管理
企业实战项目之服务器用户权限集中管理
企业实战项目之服务器用户权限集中管理
|
5月前
|
弹性计算 资源调度 API
云服务器 ECS产品使用问题之如何实现自定义页面适配移动端
云服务器ECS(Elastic Compute Service)是各大云服务商阿里云提供的一种基础云计算服务,它允许用户租用云端计算资源来部署和运行各种应用程序。以下是一个关于如何使用ECS产品的综合指南。
云服务器 ECS产品使用问题之如何实现自定义页面适配移动端
|
4月前
|
Java Spring
【Azure Spring Cloud】Spring Cloud Azure 4.0 调用Key Vault遇见认证错误 AADSTS90002: Tenant not found.
【Azure Spring Cloud】Spring Cloud Azure 4.0 调用Key Vault遇见认证错误 AADSTS90002: Tenant not found.
|
4月前
|
弹性计算 监控 网络安全
解锁云端力量:ECS全攻略,从新手到专家的实战之旅!轻松掌握云计算核心技能,驾驭数字浪潮之巅!
【8月更文挑战第22天】云计算中的弹性计算服务(ECS)让企业和开发者能按需获取虚拟服务器,灵活配置CPU、内存等资源。本文从零开始指导ECS的基础操作与高级技巧,包括实例创建、SSH连接、Web应用部署及利用ELB和自动伸缩优化性能。同时介绍监控工具和数据备份方法,帮助您全方位掌握ECS的高效运用,满足业务需求并保障数据安全。
74 3
|
5月前
|
SQL 缓存 前端开发
PHP性能优化实战:从代码到服务器的全方位攻略
【7月更文挑战第30天】在Web开发的世界里,PHP以其灵活性和易用性赢得了广泛的赞誉。然而,随着应用规模的扩大,性能问题逐渐浮现。本文将深入探讨PHP的性能优化策略,不仅涉及代码层面的精细调整,还包括服务器配置的优化技巧。我们将一起探索如何通过减少资源消耗、优化数据库交互以及利用缓存技术来提升PHP应用的性能表现。无论你是PHP新手还是资深开发者,这篇文章都将为你提供实用的优化建议,帮助你打造更快、更稳定的PHP应用。
95 4
|
4月前
|
移动开发 网络协议 编译器
实战案例3:C语言实现的HTTP服务器
实战案例3:C语言实现的HTTP服务器
271 0

热门文章

最新文章