Spring Security5 Oauth2 自定义 OAuth2 Exception
前言
spring-security5 oauth 开箱既用,认证时返回的是oauth2 标准的返回值
先来看看,默认的返回值
## 未认证
{"error": "unauthorized","error_description": "Full authentication is required to access this resource"
}
## 认证失败
{
"error": "invalid_client",
"error_description": "Bad client credentials"
}## 认证成功
{
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjbGllbnRJZCI6IndlYmFwcCIsInVzZXJfbmFtZSI6ImFkbWluIiwic2NvcGUiOlsiYWxsIl0sIkF1dGhvciI6InhpYW95YW8iLCJleHAiOjE1NjEwMjYxNDAsImF1dGhvcml0aWVzIjpbIlJPTEVfVEVTVCIsIlJPTEVfT0FVVEhfQURNSU4iXSwianRpIjoiYzhlMTA3YTItYWFlZS00NWNhLTk1NWYtM2UyZDgwZjhlZmM2IiwiY2xpZW50X2lkIjoid2ViYXBwIiwidGltZXN0YW1wIjoiMTU2MTAxODk0MDEwOCJ9.VgL4voycGdn4Fm_Ij3ZeAc9Rxdsmb_IXR14lgtJh1KE",
"token_type": "bearer",
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjbGllbnRJZCI6IndlYmFwcCIsInVzZXJfbmFtZSI6ImFkbWluIiwic2NvcGUiOlsiYWxsIl0sImF0aSI6ImM4ZTEwN2EyLWFhZWUtNDVjYS05NTVmLTNlMmQ4MGY4ZWZjNiIsIkF1dGhvciI6InhpYW95YW8iLCJleHAiOjE1NjIzMTQ5NDAsImF1dGhvcml0aWVzIjpbIlJPTEVfVEVTVCIsIlJPTEVfT0FVVEhfQURNSU4iXSwianRpIjoiMTRkNmQyMmMtZjc4OS00YzdlLTk0YjAtNTliYzhmYjlmY2NiIiwiY2xpZW50X2lkIjoid2ViYXBwIiwidGltZXN0YW1wIjoiMTU2MTAxODk0MDEwOCJ9.SOch9UKE078fRSxHYDZzXVTelVH3gpFOZUzM3KelKrk",
"expires_in": 7199,
"scope": "all",
"Author": "xiaoyao",
"clientId": "webapp",
"timestamp": "1561018940108",
"jti": "c8e107a2-aaee-45ca-955f-3e2d80f8efc6"
}
然而,这些虽是标准协议返回字段,公开给第三方做接口是没啥问题,但这个也会给我们前端去使用,这时候就会对前端造成一定的麻烦,在业务层,前端,一般会写一个拦截器,去通过一个code 或status字段,进行拦截,而认证阶段返回值就不统一了,所以我希望对于整个系统都是的,即看一下下面的期望结果的例子:
// code,msg,data 三个是我们与前端协定的整个系统中的返回值
# 未认证
{
"msg": "token不正确或已过期",
"code": "401",
"data": "Full authentication is required to access this resource",
// 下面两个是原始返回值
"error": "unauthorized",
"error_description": "Full authentication is required to access this resource"
}## 服务器错误{
"error": "invalid_request",
"code": 400,
"msg": "Internal Server Error",
"error_description": "Internal Server Error",
"data": ""
}// 登陆成功
{
"code": "200",
"msg": "success",
"data":{"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjbGllbnRJZCI6IndlYmFwcCIsInVzZXJfbmFtZSI6ImFkbWluIiwic2NvcGUiOlsiYWxsIl0sIkF1dGhvciI6InhpYW95YW8iLCJleHAiOjE1NjEwMjYxNDAsImF1dGhvcml0aWVzIjpbIlJPTEVfVEVTVCIsIlJPTEVfT0FVVEhfQURNSU4iXSwianRpIjoiYzhlMTA3YTItYWFlZS00NWNhLTk1NWYtM2UyZDgwZjhlZmM2IiwiY2xpZW50X2lkIjoid2ViYXBwIiwidGltZXN0YW1wIjoiMTU2MTAxODk0MDEwOCJ9.VgL4voycGdn4Fm_Ij3ZeAc9Rxdsmb_IXR14lgtJh1KE",
"token_type": "bearer",
"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjbGllbnRJZCI6IndlYmFwcCIsInVzZXJfbmFtZSI6ImFkbWluIiwic2NvcGUiOlsiYWxsIl0sImF0aSI6ImM4ZTEwN2EyLWFhZWUtNDVjYS05NTVmLTNlMmQ4MGY4ZWZjNiIsIkF1dGhvciI6InhpYW95YW8iLCJleHAiOjE1NjIzMTQ5NDAsImF1dGhvcml0aWVzIjpbIlJPTEVfVEVTVCIsIlJPTEVfT0FVVEhfQURNSU4iXSwianRpIjoiMTRkNmQyMmMtZjc4OS00YzdlLTk0YjAtNTliYzhmYjlmY2NiIiwiY2xpZW50X2lkIjoid2ViYXBwIiwidGltZXN0YW1wIjoiMTU2MTAxODk0MDEwOCJ9.SOch9UKE078fRSxHYDZzXVTelVH3gpFOZUzM3KelKrk",
"expires_in": 7199,
"scope": "all",
"Author": "xiaoyao",
"clientId": "webapp",
"timestamp": "1561018940108",
"jti": "c8e107a2-aaee-45ca-955f-3e2d80f8efc6"
}
}
知道自己希望想要什么,才好着手去撸代码
正文
这里面要分两步走,因为资源服务 和认证服务中是不一样的(刚开始做的我以为是一样的,后开跟了半天的源代码,才走出了这个坑)默认的主要有两个类`OAuth2AuthenticationEntryPoint` 和 `DefaultWebResponseExceptionTranslator`
//资源服务上,默认是交给这个类来处理的,包括未认证的,和错误的token
//认证服务器上未认证的也是这个
`OAuth2AuthenticationEntryPoint` //认证服务器上,上面的类不够用了,因为在security的过滤器中到处出现了
private AuthenticationEntryPoint authenticationEntryPoint = new OAuth2AuthenticationEntryPoint();
和
private WebResponseExceptionTranslator exceptionTranslator = new DefaultWebResponseExceptionTranslator();
// 认证时产生的异常交给了这个类处理返回结果
`DefaultWebResponseExceptionTranslator`
先来看资源服务吧,这个比较简单
首先自定义一个`AuthenticationEntryPoint`
public class CustomAuthenticationEntryPoint extends OAuth2AuthenticationEntryPoint {private WebResponseExceptionTranslator<?> exceptionTranslator = new DefaultWebResponseExceptionTranslator();@Overridepublic void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException, ServletException {// super () //doHandle(request, response, e)// log.debug("token 验证失败处理器")BaseResult<Object> res = new BaseResult<>();res.setCode(HttpStatus.UNAUTHORIZED.value());ResponseEntity<?> result = null;Map<String,String> resp = new HashMap<> (5);try {result = exceptionTranslator.translate(e);result = enhanceResponse(result, e);// 这里用的guava切分字符串转map,但不能直接返回赋值给 result ,不多做解释,跟踪一下源码就知道Map<String, String> split = Splitter.on(",").trimResults().trimResults(CharMatcher.is('\"')).withKeyValueSeparator("=\"").split(result.getBody().toString());// 不去改变原来父类里面返回的属性和值split.forEach((x,y) ->resp.put(x.trim(), y));} catch (Exception ex) {log.error("结果解析异常", ex);}resp.put("code", res.getCode()+"");resp.put("msg", "token不正确或已过期");resp.put("data",e.getMessage());HttpResponseUtil.writeResult(response, resp, HttpStatus.UNAUTHORIZED);}}
然后在资源费器的配置中配置即可
public class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {@Autowiredprivate BaseAuthenticationConfig baseAuthenticationConfig;@Autowired(required = false)private AuthenticationEntryPoint authenticationEntryPoint;@Overridepublic void configure(HttpSecurity http) throws Exception {log.debug("Using ResourceServerConfiguration configure(HttpSecurity). " );baseAuthenticationConfig.configure(http);http.authorizeRequests().antMatchers(baseAuthenticationConfig.getIgnoreUrl()).permitAll().antMatchers("/login","/error","/oauth/token","/process/login","/logout","/login.html").permitAll()// .antMatchers("/**").authenticated().antMatchers("/sys/sys-user/info").authenticated().and().csrf().disable();}// 在这儿配@Overridepublic void configure(ResourceServerSecurityConfigurer resources) throws Exception {// 配置认证失败处理器if(authenticationEntryPoint != null){resources.authenticationEntryPoint(authenticationEntryPoint);}}}
认证服务器,稍微麻烦点
首先将自定义的 `AuthenticationEntryPoint`在http config中配置上
public void configure(HttpSecurity http) throws Exception {http.formLogin().loginPage("/login").loginProcessingUrl("/user/login").successHandler(authenticationSuccessHandler).failureHandler(authenticationFailureHandler).and().authorizeRequests().antMatchers("/login","/user/login","/user/logout","/oauth/token").permitAll().and().exceptionHandling().accessDeniedHandler(accessDeniedHandler).and()// 这里设置的 只是用户没有登录的 情况下 没有token 的情况下,异常给下面的处理.exceptionHandling().authenticationEntryPoint(authenticationEntryPoint);}
好了在登录时认证失败会产生各种异常,但最有都有一个`ExceptionTranslationFilter`处理,这个是spring security的基础知识(插张图)
这个是访问认证服务器 资源的时候,还是交给自定义的AuthenticationEntryPoint 处理的
看一下登陆的时候
登陆失败
-- 未完 待续,主要原因在用户通过授权码登陆异常时没有走自定义的异常处理类
Spring Security5 Oauth2 自定义 OAuth2 Exception相关推荐
- Spring Security OAuth2——自定义OAuth2第三方登录(Gitee)并与UsernamePassword登录关联解决方案
前文:Spring Security OAuth2--自定义OAuth2第三方登录(Gitee) Maven 主要 <!--Spring Security--><dependency ...
- Spring Security OAuth2——自定义OAuth2第三方登录(Gitee)
官方文档 https://docs.spring.io/spring-security/site/docs/current/reference/html5/#oauth2login-custom-pr ...
- 如何使用okta作为认证方配置Spring Boot 2 Security5集成的OAuth2登录到我们自己的工程项目------范例3
本篇范例在范例2的基础上持续集成扩展 注册账号的地址如下: https://developer.okta.com/signup/ 注册成功要求你用临时密码(发送到你邮箱的)激活你的注册账号 此网站会分 ...
- 使用Spring Boot 2使用OAuth2和不透明令牌进行集中授权
如果您正在寻找JWT实施,请点击此链接 本指南逐步介绍了使用Spring Boot 2创建集中式身份验证和授权服务器的过程,还将提供演示资源服务器. 如果您不熟悉OAuth2,建议您阅读此书. 先决条 ...
- 使用Spring Boot 2通过OAuth2和JWT进行集中授权
本指南逐步介绍了使用Spring Boot 2创建集中式身份验证和授权服务器的过程,还将提供演示资源服务器. 如果您不熟悉OAuth2,建议您阅读此书. 先决条件 JDK 1.8 文本编辑器或您喜欢的 ...
- Spring Boot Security 整合 OAuth2 设计安全API接口服务
OAuth2概述 oauth2根据使用场景不同,分成了4种模式 授权码模式(authorization code) 简化模式(implicit) 密码模式(resource owner passwor ...
- (附源码)Spring Boot 框架整合 OAuth2 实现单点登录 SSO 详细完整源码教程!
1. 前言 技术这东西吧,看别人写的好像很简单似的,到自己去写的时候就各种问题,"一看就会,一做就错".网上关于实现SSO的文章一大堆,但是当你真的照着写的时候就会发现根本不是那 ...
- Spring Security(5) 整合OAuth2
文章目录 一.前言 二.什么是OAuth2? 三.应用场景 四.三部分 五.四种授权模式 1. 授权码模式(authorization code) 2. 简化模式(implicit) 3. 密码模式( ...
- Spring Security 5.x+OAuth2.0+OIDC1.0
Spring Security+OAuth2.0+OIDC1.0 文章目录 Spring Security+OAuth2.0+OIDC1.0 前言 一.准备工作 1.Spring Security相关 ...
最新文章
- 六条规则让你的ML模型部署的更快
- ubuntu16.04在英文状态下安装中文语言包的过程(法一:图形界面的方式) 以及 安装中文语言包后无法选择汉语问题的解决
- 【转载】python3安装scrapy之windows32位爬坑
- leetcode868
- 人眼中亮斑的检测、定位和去除(3)
- Yearning v1.4.2 发布,SQL审核平台
- ppt倒计时_年终会议做一个这样的倒计时PPT,保证惊艳全场!1分钟就能学会
- 图书馆管理系统项目思路
- openGauss 正式开源并成立开源社区
- sql 系统 存储过程的使用方法 转载
- 【干货】统计学思维导图
- 架构之路(五):忘记数据库
- Cartesian k-means论文理解
- Atitit.软件硕士 博士课程 一览表 attilax 总结
- layui radio 赋初始值
- win环境sftp软件_Windows环境下使用bitvise架构sftp服务器
- 【Get Up&Move】MMD镜头+动作打包下载.zip
- 什么是手机号码姓名实名认证 手机号码查姓名 手机号实名认证API
- epub是什么文件?epub文件怎么打开?
- 埋石图根点lisp代码_速腾矿图 用户手册.pdf