)
此文章用到的版本
1 | yaml复制代码spring-boot : 2.6.8 |
引入依赖包(gradle) maven 请自行转换
1 | java复制代码 |
先说说原理
UsernamePasswordAuthenticationFilter 继承 AbstractAuthenticationProcessingFilter
AbstractAuthenticationProcessingFilter.doFilter() 会执行 抽象方法attemptAuthentication ()
通过观察发现 UsernamePasswordAuthenticationFilter 会拦截 POST /login 的请求
然后通过会通过Http parameter 获取 username 和 password 参数的值执行鉴权认证
1 | java复制代码 public static final String SPRING_SECURITY_FORM_USERNAME_KEY = "username"; |
成功会执行 SavedRequestAwareAuthenticationSuccessHandler 重定向到指定url
1 | java复制代码public class SavedRequestAwareAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler { |
失败会执行 SimpleUrlAuthenticationFailureHandler
1 | java复制代码 @Override |
这种方式不兼容json方式提交的登录 而且不能返回token 供前端使用 所以我们需要改造此Filter
实现思路:
- 拦截Post /login 请求
- 获取请求中的body参数 username 以及password
- 返回 UsernamePasswordAuthenticationFilter 不携带权限集合
- 重写 UserDetailsService 查询数据库
- 重写 AuthenticationSuccessHandler 登录成功后返回jwt token令牌
- 重写 AuthenticationFailureHandler 失败返回失败原因 例如:密码错误,账户锁定, 账户不存在
定义token常量
1 | java复制代码public class SecurityConstants |
实现工具类 JWTUtils 用户生成、解析token
1 | java复制代码@Component |
开始实现身份认证过滤器(JWTAuthenticationFilter) 继承 UsernamePasswordAuthenticationFilter 重写 attemptAuthentication 方法
1 | java复制代码public class JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilter |
重写UserDetailService
返回一个测试用户 用户名:123 密码:123 角色权限: ROLE_ADMIN
1 | java复制代码@Service |
重写 AuthenticationSuccessHandler 成功后将生成的 token 放入 response header
1 | java复制代码@Component |
重写 AuthenticationFailureHandler 返回账户失败原因
1 | java复制代码@Component |
改造BasicAuthenticationFilter基于JWT解析 实现权限认证
认证过滤器 BasicAuthenticationFilter
header里头有Authorization
,而且value是以Basic
开头的,则走BasicAuthenticationFilter
,提取参数构造UsernamePasswordAuthenticationToken
进行认证,成功则填充SecurityContextHolder的Authentication
而我们要做的是 header里头有Authorization
,而且value是以Bearer
开头的, 解析jwt token填充SecurityContextHolder的Authentication
1 | java复制代码 @Override |
开始实现 继承BasicAuthenticationFilter 并重写 doFilterInternal 方法
getAuthentication 获取 request header中的token 解析成username 并调用Userservice中的loadUserByName方法返回User鉴权信息
装入SecurityContextHolder
1 | java复制代码public class JWTAuthorizationFilter extends BasicAuthenticationFilter |
CustomAccessDeniedHandler 非匿名下的错误拦截器
1 | java复制代码@Component |
CustomAuthenticationEntryPoint 匿名下的错误拦截器
1 | java复制代码@Component |
OK 准备大功告成, 最后设定一下Spring Security的配置
1 | java复制代码@EnableWebSecurity |
编写测试Controller
1 | java复制代码@RestController |
测试 /login 登录
输入错误的账号密码
)
编辑
输入正确的账号密码
)
编辑
放行请求/sign-up
)
编辑
身份鉴权认证
无token
)
编辑
错误token
)
编辑
正确token 无权限
)
编辑
正确token 有权限
)
编辑
本文转载自: 掘金