在了解过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自定义认证器的更多相关文章

  1. Spring Security自定义认证页面(动态网页解决方案+静态网页解决方案)--练气中期圆满

    写在前面 上一回我们简单分析了spring security拦截器链的加载流程,我们还有一些简单的问题没有解决.如何自定义登录页面?如何通过数据库获取用户权限信息? 今天主要解决如何配置自定义认证页面 ...

  2. spring security自定义指南

    序 本文主要研究一下几种自定义spring security的方式 主要方式 自定义UserDetailsService 自定义passwordEncoder 自定义filter 自定义Authent ...

  3. Spring Security 接口认证鉴权入门实践指南

    目录 前言 SpringBoot 示例 SpringBoot pom.xml SpringBoot application.yml SpringBoot IndexController SpringB ...

  4. Spring Security 自定义登录认证(二)

    一.前言 本篇文章将讲述Spring Security自定义登录认证校验用户名.密码,自定义密码加密方式,以及在前后端分离的情况下认证失败或成功处理返回json格式数据 温馨小提示:Spring Se ...

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

    本文完整代码下载点击 一. 前言 相信了解过我或者看过我之前的系列文章应该多少知道点我写这些文章包括创建 有来商城youlai-mall 这个项目的目的,想给那些真的想提升自己或者迷茫的人(包括自己- ...

  6. (二)spring Security 自定义登录页面与校验用户

    文章目录 配置 security 配置下 MVC 自定义登录页面 自定义一个登陆成功欢迎页面 效果图 小结: 使用 Spring Boot 的快速创建项目功能,勾选上本篇博客需要的功能:web,sec ...

  7. 解决Spring Security自定义filter重复执行问题

    今天做项目的时候,发现每次拦截器日志都会打两遍,很纳闷,怀疑是Filter被执行了两遍.结果debug之后发现还真是!记录一下这个神奇的BUG! 问题描述 项目中使用的是Spring-security ...

  8. 最简单易懂的Spring Security 身份认证流程讲解

    最简单易懂的Spring Security 身份认证流程讲解 导言 相信大伙对Spring Security这个框架又爱又恨,爱它的强大,恨它的繁琐,其实这是一个误区,Spring Security确 ...

  9. 02 spring security 自定义用户认证流程

    1. 自定义登录页面 (1)首先在static目录下面创建login.html       注意: springboot项目默认可以访问resources/resources, resources/s ...

随机推荐

  1. 【FAQ】HMS Core广告服务:如何获取正式广告位ID以及流量变现的受限情况

    HMS Core广告服务开发指南中提到"xxxx为测试专用的广告位ID,App正式发布时需要改为正式的广告位ID",那么今天咱们就来说说,怎么获取正式的广告位ID. 测试广告位ID ...

  2. 使用aspnetcore前后端分离开发,你一定要知道这个

    前言 用过Vue单页面应用开发的,一定都知道Vue-router这个路由组件,它支持hash和history两种模式. HTML5 History 模式 vue-router 默认 hash 模式 - ...

  3. 集合篇-ConcurrentHashMap

    点赞再看,养成习惯,微信搜索「小大白日志」关注这个搬砖人. 文章不定期同步公众号,还有各种一线大厂面试原题.我的学习系列笔记. jdk1.7和jdk1.8中ConcurrentHashMap的区别? ...

  4. Python学习-Day1(Typora软件与计算机)

    学习总括 Typora软件介绍(markdown语法) 相关拓展知识 文件的后缀名是什么? 什么是语言? 什么是编程语言? 什么是编程?(程序员写代码的本质) 计算机的五大组成部分 计算机的本质 计算 ...

  5. 攻防世界-MISC:SimpleRAR

    这是攻防世界新手练习区的第十题,题目如下: 点击下载附件1,是一个RAR文件,解压一下,得到flag.txt文件,激动的点进去看一下 好吧,就知道没有这么简单,用010editor打开这个RAR文件, ...

  6. 类变量_main方法_代码块

    需要解决的问题: 统计一共创建了多少个对象 类变量(静态变量) 被所有对象共享 可以通过 类名(推荐)|对象名.变量名 方式来访问 main main 方法 是虚拟机在调用  args 传值灵活 结果 ...

  7. QMetaObject::connectSlotsByName: No matching signal for XXX 原理探究

    问题引出: 在尝试实现<Qt5.9 c++开发指南>混合UI编程章节时,用纯代码形式实现了个小按钮,然后加了个对应的槽函数,运行时就提示了这个信息. 原因探究: 首先查阅官方手册中的说明: ...

  8. 【干货】BIOS、UEFI、MBR、GPT、GRUB 到底是什么意思?

    公众号关注 「开源Linux」 回复「学习」,有我为您特别筛选的学习资料~ 01 前言 在学习 Linux 系统启动原理之前,我们先了解下与操作系统启动相关的几个概念. 02 与操作系统启动相关的几个 ...

  9. Linux入门进阶 - 如何在Linux中使用export命令

    来自:Linux迷链接:https://www.linuxmi.com/linux-export.html Linux export命令会标记哪些值需要传递给一组子进程.这是bash shell提供的 ...

  10. Windows Terminal无法加载WSL [process exited with code 4294967295 (0xffffffff)]

    在Windows Terminal中WSL无法打开错误代码是 process exited with code 4294967295 (0xffffffff),但在命令行中 通过 "C:\W ...