Spring Security自定义认证器
在了解过Security的认证器后,如果想自定义登陆,只要实现AuthenticationProvider还有对应的Authentication就可以了
Authentication
首先要创建一个自定义的Authentication,Security提供了一个Authentication的子类AbstractAuthenticationToken
我们实现这个类可以了,他已经实现了Authentication的一些方法
public class NamePassAuthenticationToken extends AbstractAuthenticationToken {
private static final long serialVersionUID = 520L;
private final Object principal;
private Object credentials;
//提供第一次进来的构造方法
public NamePassAuthenticationToken(Object principal, Object credentials) {
super((Collection)null);
this.principal = principal;
this.credentials = credentials;
this.setAuthenticated(false);
}
//提供填充Authentication的构造方法
public NamePassAuthenticationToken(Object principal, Object credentials, Collection<? extends GrantedAuthority> authorities) {
super(authorities);
this.principal = principal;
this.credentials = credentials;
super.setAuthenticated(true);
}
@Override
public Object getCredentials() {
return this.credentials;
}
@Override
public Object getPrincipal() {
return this.principal;
}
@Override
public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
if (isAuthenticated) {
throw new IllegalArgumentException("Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead");
} else {
super.setAuthenticated(false);
}
}
@Override
public void eraseCredentials() {
super.eraseCredentials();
this.credentials = null;
}
}
这个类关键就是一个是认证的,一个没认证的的构造器
AuthenticationProvider
接着是AuthenticationProvider,需要实现他的authenticate方法
@Setter
public class NamePassAuthenticationProvider implements AuthenticationProvider {
private CustomUserDetailsService userDetailsService;
private PasswordEncoder passwordEncoder;
@Override
//具体认证逻辑
public Authentication authenticate(Authentication authentication) {
NamePassAuthenticationToken authenticationToken = (NamePassAuthenticationToken) authentication;
String username = (String) authenticationToken.getPrincipal();
String password = (String) authenticationToken.getCredentials();
//让具体认证类去认证
UserDetails user = userDetailsService.loadUserByUsername(username);
boolean matches = passwordEncoder.matches(password, user.getPassword());
if (!matches) {
ResMsg.throwException(AuthExceptionGroup.AUTH_ERROR);
}
//填充Authentication
NamePassAuthenticationToken authenticationResult = new NamePassAuthenticationToken(user, password, user.getAuthorities());
authenticationResult.setDetails(authenticationToken.getDetails());
return authenticationResult;
}
@Override
//指定具体的Authentication
//根据你指定的Authentication来找到具体的Provider
public boolean supports(Class<?> authentication) {
return NamePassAuthenticationToken.class.isAssignableFrom(authentication);
}
}
SecurityConfigurerAdapter
接着就是填充配置了
@Component
public class NamePassAuthenticationSecurityConfig extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {
@Autowired
private CustomUserDetailsService customUserDetailsService;
@Autowired
private PasswordEncoder passwordEncoder;
@Override
public void configure(HttpSecurity http) {
//phonePass provider
NamePassAuthenticationProvider provider = new NamePassAuthenticationProvider();
provider.setUserDetailsService(customUserDetailsService);
provider.setPasswordEncoder(passwordEncoder);
http.authenticationProvider(provider);
}
}
接下来就是导入配置了
通常都会有一个实现了WebSecurityConfigurerAdapter的配置类
把配置类注入进来
@Autowired
private NamePassAuthenticationSecurityConfig namePassAuthenticationSecurityConfig;
protected void configure(HttpSecurity http) throws Exception {
http.apply(namePassAuthenticationSecurityConfig);
}
UserDetailsService
UserDetailsService是具体的认证实现类
这个类就非常熟悉了,只需要实现他的loadUserByUsername方法,就可以实现认证了
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
AuthmsViewAccount account = accountService.getAccount(username);
if(account == null) {
ResMsg.throwException(AUTH_ERROR);
}
if (account.getStatus() != 1) {
ResMsg.throwException(ACCOUNT_HAS_BANED);
}
String spliceStaffInfo = String.format("%d-%s",account.getAccountId(),account.getUsername());
//只要Collection<? extends GrantedAuthority> authorities
//这个参数不为空,就表明认证通过,所以空集合也可以通过
return new User(spliceStaffInfo,account.getPassword(), AuthorityUtils.NO_AUTHORITIES);
}
把认证结果填充到上下文中
TokenFilter
如果结合了Token,那么需要从token中识别该用户
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
String bearerToken = resolveToken(request);
if (bearerToken != null && !"".equals(bearerToken.trim()) && SecurityContextHolder.getContext().getAuthentication() == null) {
//从redis中获取该用户
NamePassAuthenticationToken namePassAuthenticationToken = authRedisHelper.get(bearerToken);
if(namePassAuthenticationToken != null) {
//将信息保存到上下文中
SecurityContextHolder.getContext().setAuthentication(namePassAuthenticationToken);
}
}
chain.doFilter(request, response);
}
private String resolveToken(HttpServletRequest request) {
String bearerToken = request.getHeader("Authorization");
if (StringUtils.hasText(bearerToken) && bearerToken.startsWith(TOKEN_PREFIX)) {
return bearerToken.substring(7);
}
return null;
}
public NamePassAuthenticationToken get(String bearerToken){
String spliceStaffInfo = (String)redisRepository.get(formatKey(bearerToken));
if(spliceStaffInfo == null) {
return null;
}
return new NamePassAuthenticationToken(new AuthStaff(spliceStaffInfo),null,AuthorityUtils.NO_AUTHORITIES);
}
登录过程
在登录的时候,就需要用到这个自定义的认证器了
// 通过用户名和密码创建一个 Authentication 认证对象,实现类为 NamePassAuthenticationToken
NamePassAuthenticationToken authenticationToken = new NamePassAuthenticationToken(user.getUsername(), user.getPassword());
//通过 AuthenticationManager(默认实现为ProviderManager)的authenticate方法验证 Authentication 对象
//AuthenticationManager会通过你传入的authenticationToken来找到具体的Provider
Authentication authentication = authenticationManager.authenticate(authenticationToken);
//填充用户信息到secrity中的user里
User principal = (User) authentication.getPrincipal();
//获取认证后的信息
NamePassAuthenticationToken namePassAuthenticationToken = new NamePassAuthenticationToken(new AuthStaff(principal.getUsername()), null, authentication.getAuthorities());
// 生成token
String bearerToken = IdUtil.fastSimpleUUID();
// 加载到reids
authRedisHelper.set(bearerToken, namePassAuthenticationToken);
这样就实现了自定义的认证器了
Spring Security自定义认证器的更多相关文章
- Spring Security自定义认证页面(动态网页解决方案+静态网页解决方案)--练气中期圆满
写在前面 上一回我们简单分析了spring security拦截器链的加载流程,我们还有一些简单的问题没有解决.如何自定义登录页面?如何通过数据库获取用户权限信息? 今天主要解决如何配置自定义认证页面 ...
- spring security自定义指南
序 本文主要研究一下几种自定义spring security的方式 主要方式 自定义UserDetailsService 自定义passwordEncoder 自定义filter 自定义Authent ...
- Spring Security 接口认证鉴权入门实践指南
目录 前言 SpringBoot 示例 SpringBoot pom.xml SpringBoot application.yml SpringBoot IndexController SpringB ...
- Spring Security 自定义登录认证(二)
一.前言 本篇文章将讲述Spring Security自定义登录认证校验用户名.密码,自定义密码加密方式,以及在前后端分离的情况下认证失败或成功处理返回json格式数据 温馨小提示:Spring Se ...
- Spring Cloud实战 | 第九篇:Spring Cloud整合Spring Security OAuth2认证服务器统一认证自定义异常处理
本文完整代码下载点击 一. 前言 相信了解过我或者看过我之前的系列文章应该多少知道点我写这些文章包括创建 有来商城youlai-mall 这个项目的目的,想给那些真的想提升自己或者迷茫的人(包括自己- ...
- (二)spring Security 自定义登录页面与校验用户
文章目录 配置 security 配置下 MVC 自定义登录页面 自定义一个登陆成功欢迎页面 效果图 小结: 使用 Spring Boot 的快速创建项目功能,勾选上本篇博客需要的功能:web,sec ...
- 解决Spring Security自定义filter重复执行问题
今天做项目的时候,发现每次拦截器日志都会打两遍,很纳闷,怀疑是Filter被执行了两遍.结果debug之后发现还真是!记录一下这个神奇的BUG! 问题描述 项目中使用的是Spring-security ...
- 最简单易懂的Spring Security 身份认证流程讲解
最简单易懂的Spring Security 身份认证流程讲解 导言 相信大伙对Spring Security这个框架又爱又恨,爱它的强大,恨它的繁琐,其实这是一个误区,Spring Security确 ...
- 02 spring security 自定义用户认证流程
1. 自定义登录页面 (1)首先在static目录下面创建login.html 注意: springboot项目默认可以访问resources/resources, resources/s ...
随机推荐
- SpringCloudAlibaba注册中心与配置中心之利器Nacos实战与源码分析(上)
不断踩坑并解决问题是每个程序员进阶到资深的必要经历并以此获得满足感,而不断阅读开源项目源码和总结思想是每个架构师成长最佳途径.本篇拉开SpringCloud Alibaba最新版本实战和原理序幕,以工 ...
- 现代 CSS 解决方案:Modern CSS Reset
在早年间(其实也不是很早),写过几篇关于 CSS Reset 的文章 - reset.css 知多少. 详细描述了当时业界比较常用的,两个 CSS reset 方案:reset.css 与 Norma ...
- python 安装各种库的镜像
让python pip使用国内镜像#国内源:#清华:https://pypi.tuna.tsinghua.edu.cn/simple 阿里云:https://mirrors.aliyun.com/py ...
- Linux下快速拷贝单个大文件的秘诀
#include <stdio.h> #include <unistd.h> #include <fcntl.h> #include <stdlib.h> ...
- 【原创】浅谈指针(十二)关于static(上)
0.前言 这个系列基本上是一月一更到两月一更 今天写一篇关于static的,内含大量干货,做好准备 1.基础知识的回顾 1.1.内存的种类 一般来说,我们之前已经讲过的变量(或者说是内存)可以大体分为 ...
- 从0开始用webpack开发antd,react组件库npm包并发布
一.初始化一个npm包 1.新建一个文件夹(名称随意,建议和报名一致),输入命令 :npm init -y 会自动生成一个包的说明文件 package.json如下(本文以scroll-antd-ta ...
- come on! 基于LinkedHashMap实现LRU缓存
/** * @Description 基于LinkedHashMap实现一个基于'LRU最近最少使用'算法的缓存,并且最多存MAX个值 * @Author afei * @date:2021/4/25 ...
- 【论文笔记】A review of applications in federated learning(综述)
A review of applications in federated learning Authors Li Li, Yuxi Fan, Mike Tse, Kuo-Yi Lin Keyword ...
- ONNXRuntime学习笔记(三)
接上一篇完成的pytorch模型训练结果,模型结构为ResNet18+fc,参数量约为11M,最终测试集Acc达到94.83%.接下来有分两个部分:导出onnx和使用onnxruntime推理. 一. ...
- 调试F9/F10/F11/F8
这篇随笔记录来自于实现活动促销页弹幕过程学习: // 页面加载完初始化方法$(function () { GetCustList(); createBarrage(); }) // 某功能的初始化方法 ...