![4d1ba5106a43ffba40675519b3b5877c.png](https://i-blog.csdnimg.cn/blog_migrate/b159959d04903799e63214301bae104c.jpeg)
在前端的文章中,我们实现了使用 SpringSecurity 实现登录鉴权,并使用数据库存储用户信息,实现登录鉴权
SpringBoot2.0实战(24)整合SpringSecurity之最简登录方法鉴权
SpringBoot2.0实战(25)整合SpringSecurity之数据库登录鉴权
登录页都是使用 SpringSecurity 提供的默认登录,入参为 username 及 password,前端通过 form 表单提交请求,后台使用 request.getParameter() 获取填写数据。
目前,在实际项目中,更多的企业选择使用前后端分离的项目架构,前后端数据交互选择使用 application/json 方式进行传递,同时,在实际的项目中,拥有更多形式的登录,比如手机号/短信验证码,不能使用单一的 usernamepassword 结构进行处理,需要自行定义入参结构。
目标
本章将整合 SpringSecurity 实现使用自定义格式进行登录,并使用 json 方式进行前后端交互。
准备工作
创建用户表 user、角色表 role、用户角色关系表 user_role
![b1365e05fcd3f94c76e132843f7a089c.png](https://i-blog.csdnimg.cn/blog_migrate/a61e8a60a8628a8b0ffb6e80ef43d00c.jpeg)
操作步骤
添加依赖
引入 Spring Boot Starter 父工程
![51fe5976ad0a931305a300a93b645013.png](https://i-blog.csdnimg.cn/blog_migrate/9bff7d3ca071e608643a8464d472814e.jpeg)
添加 springSecurity 及 mybatisPlus 的依赖,添加后的整体依赖如下
![c1a2035f810399f6b9ef2c379147898e.png](https://i-blog.csdnimg.cn/blog_migrate/37244deefde38739811d8afedeab6ccb.jpeg)
配置
配置一下数据源
![29dd50c501f9430902b63d0470d8c6fc.png](https://i-blog.csdnimg.cn/blog_migrate/593103e263d6d00470cafd175d856dc0.jpeg)
编码
实体类
角色实体类 Role,实现权限接口 GrantedAuthority
![4fe5d8de265f1e25fcc9ed97bfa5c7bf.png](https://i-blog.csdnimg.cn/blog_migrate/ebc00b75faa1a58a178434436b58399b.jpeg)
用户实体类 user,实现权限接口 UserDetails,主要方法是 getAuthorities,用于获取用户的角色列表
![98ec6ebf8bec2c6bccea31ffccd16f21.png](https://i-blog.csdnimg.cn/blog_migrate/96cdfda6b04fedc796bb786db3cba342.jpeg)
用户角色关系实体
![97edbe2683e58c9cc509d91750062271.png](https://i-blog.csdnimg.cn/blog_migrate/964e7f576e371658fee035bd8f6eaed1.jpeg)
Repository 层
分别为三个实体类添加 Mapper
![d0a86c15c03f505e6ac5485d097b959d.png](https://i-blog.csdnimg.cn/blog_migrate/bc3d5fe5fc895bcd822872aa09b568b4.jpeg)
实现 UserDetailsService 接口
UserDetailsService 是 SpringSecurity 提供的登陆时用于根据用户名获取用户信息的接口
![e23f6539849a1a63e2ec684d90f2b1ae.png](https://i-blog.csdnimg.cn/blog_migrate/575d28ceccdc66a20d10e6ec83c32850.jpeg)
自定义登录参数格式
![3f273526322abb25fdd2904ccb23a2f7.png](https://i-blog.csdnimg.cn/blog_migrate/b68ad283689d6c9a6da591dd66d20609.jpeg)
自定义登录过滤器
继承 SpringSecurity 提供的 AbstractAuthenticationProcessingFilter 类,实现 attemptAuthentication 方法,用于登录校验。
本例中,模拟前端使用 json 格式传递参数,所以通过 objectMapper.readValue 的方式从流中获取入参,之后借用了用户名密码登录的校验,并返回权限对象
![df32c7e06ae88a9dd4f609b94012d7f7.png](https://i-blog.csdnimg.cn/blog_migrate/6f3f664a88f28da25deecf8e18ca6f17.jpeg)
自定义登陆成功后处理
实现 SpringSecurity 提供的 AuthenticationSuccessHandler 接口,使用 JSON 格式返回
![67e931e80ebf4a72ebae50326dc7a17f.png](https://i-blog.csdnimg.cn/blog_migrate/82f3df5460707571c798c1ef277bcad9.jpeg)
自定义登陆失败后处理
实现 SpringSecurity 提供的 AuthenticationFailureHandler 接口,使用 JSON 格式返回
![488770f2c50f8b49692146201c03766a.png](https://i-blog.csdnimg.cn/blog_migrate/d7115f6b16594ced920b8bb78c69891e.jpeg)
自定义权限校验失败后处理
登陆成功之后,访问接口之前 SpringSecurity 会进行鉴权,如果没有访问权限,需要对返回进行处理。实现 SpringSecurity 提供的 AccessDeniedHandler 接口,使用 JSON 格式返回
![37bcffc5fdbf90c0378228c4f1400182.png](https://i-blog.csdnimg.cn/blog_migrate/56d400e0ea4d73af0da29099c93c5ddf.jpeg)
自定义未登录后处理
实现 SpringSecurity 提供的 AuthenticationEntryPoint 接口,使用 JSON 格式返回
![1ff0ef5deaf0fb7ad32b5684fe003d96.png](https://i-blog.csdnimg.cn/blog_migrate/68c673cb639cd1687e9dabeabffacd60.jpeg)
注册
在 configure 方法中调用 addFilterAfter 方法,将自定义的 jsonAuthenticationFilter 注册进 SpringSecurity 的过滤器链中。
![36e7ce2ef2ffda2e259415767cb6dd5a.png](https://i-blog.csdnimg.cn/blog_migrate/2f8c377c7a69d9fe674ee63170971eba.jpeg)
启动类
![ee3b53458a4e483a44493e2686cc1d95.png](https://i-blog.csdnimg.cn/blog_migrate/feda5047aad6264c703a891669ddcbeb.jpeg)
验证结果
初始化数据
![e8ae37a98de3f9307a7cee2abe578b98.png](https://i-blog.csdnimg.cn/blog_migrate/47dbb590cb4ad0d60d4f52a889019c08.jpeg)
源码地址
本章源码 : https://gitee.com/gongm_24/spring-boot-tutorial.git
扩展
前面的示例只是使用了一个自定义结构去接收前端表单数据,但是处理还是使用的 username/password 那一套,调用 getAuthenticationManager().authenticate() 去进行入参的校验,AuthenticationManager 中维护一个 AuthenticationProvider 列表,每一个 AuthenticationProvider 会支持一个 Token 类型,username/password 则是使用的 UsernamePasswordAuthenticationToken,并为之提供了 AbstractUserDetailsAuthenticationProvider 进行登录校验,AbstractUserDetailsAuthenticationProvider 的实现类就是 DaoAuthenticationProvider,注册的时候通过 auth.userDetailsService(userService) 方法调用,会将 DaoAuthenticationProvider 加入至 AuthenticationManager 中维护的 AuthenticationProvider 列表中。
如果需要完全自定义入参结构,按如下操作即可
- 自定义 AbstractAuthenticationToken 实现类
- 自定义 AuthenticationProvider 实现类,并支持自定义的 AbstractAuthenticationToken 实现类,具体实现可以参考 AbstractUserDetailsAuthenticationProvider
- 自定义 AuthenticationProcessingFilter 实现类,需要定义请求地址,接收入参,组装 Token,调用 getAuthenticationManager().authenticate()
- 将自定义 AuthenticationProvider 实现类加入至 AuthenticationManager 中维护的 AuthenticationProvider 列表中。
- 将自定义 AuthenticationProcessingFilter 实现类加入至过滤器列表中,置于 UsernamePasswordAuthenticationFilter 之后。