Spring Security如何实现升级密码加密方式详解

目录
  • 本章内容
  • 密码加密方式怎么升级?
  • 升级方案源码
  • 实战
    • 第一种方式: Spring Bean
      • 他是怎么自动升级到BCrypt加密方式的?
    • 第二种方式: 多继承接口方式
    • 第三种方式: HttpSecurity直接添加

本章内容

  • 密码加密方式怎么升级?
  • spring security底层怎么实现的密码加密方式升级?

密码加密方式怎么升级?

前面我们学过DelegatingPasswordEncoder类,但是不清楚他到底是做什么的,我也没讲的很清楚。所以呢,我们就重新再讲一讲它的另一个实际应用。

小明呢,有一天在刷新闻。突然收到了一篇关于MD5加密存在重大漏洞的报告, 而最佳的代替加密方案是BCrypt。此时小明慌了。

因为他项目里面就是用着MD5加密。那现在怎么办呢?小明的用户体量比较大,你不可能叫客户/程序员一个个去改是吧?

spring security就提供了一种这种情况的解决方案。

在用户登录你的账户时,自动的升级您的密码加密方式。比如说从MD5加密方式变成BCrypt

但是呢,这种方式有一个前提。您数据库的用户密码必须要有ID,也就是花括号的那一部分{noop}123456

当然如果花括号没有,然后数据体量就比较大,你只能重写DelegatingPasswordEncoder

抄代码的地方就在PasswordEncoderFactories#createDelegatingPasswordEncoder, 也就是你数据库中的密码没有花括号部分(拿不到ID)的情况下, 使用BCryptPasswordEncoder

小白: "那在spring security中哪一部分定义了这项功能?"

升级方案源码

首先我们得思考。什么情况下才会进行密码升级?

按照常理来说,应该是在用户登录成功之后进行密码升级。所以我们在找源码的时候,应该先去找认证成功的那部分源码,绝对能找到这部分功能。

我第一反应找UsernamePasswordAuthenticationFilterAbstractAuthenticationProcessingFilter抽象类的doFilter方法

但是不幸的是这里找不到我们想要的功能。所以我立即反应起来这项功能应该是在认证器这边。

DaoAuthenticationProviderAbstractUserDetailsAuthenticationProvider

找到的认证成功之后,他执行的一段函数。可以明显的看出有更新密码的过程。

这里只要保证upgradeEncoding == true,那么就可以进入更新密码的过程。

这里我们看到了一段代码this.userDetailsPasswordService, 可以百分百确定,我们的功能就在这个接口里面。

至于if的另一个函数upgradeEncoding, 你只要知道用户输入密码和数据库密码ID不同就为 true, 相同就为 false, 当然还有ID相同不同长度的解决方案, 这里就不细谈了

public interface UserDetailsPasswordService {
   UserDetails updatePassword(UserDetails user, String newPassword);
}

如果你英文能力比较强的话,可以直接去查看这个接口上面就会有注释,内容就是修改用户名的密码就这么简单。

既然已经知道这个接口的存在了,那现在的问题是怎么让spring security调用我们所实现的这个接口呢?

我现在罗列出三张图片。就可以从这三张图片中总结出三种加载我们实现类的方法。

实战

第一种方式: Spring Bean

public class UserService1 implements UserDetailsService {
	@Resource
	private UsersMapper usersMapper;
	@Override
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
		Optional<Users> optionalUsers = Optional.ofNullable(usersMapper.loadUserByUsername(username));
		return optionalUsers.orElseThrow(() -> new UsernameNotFoundException("找不到用户名"));
	}
}
@Bean
public UserService1 userService1() throws Exception {
    return new UserService1();
}

这种方式对应着上面第3张图。

那现在就会有人问的。我并没有写出从MD5加密方式升级到BCrypt加密方式。他是怎么自动升级到BCrypt加密方式的?

带着问题看源码

他是怎么自动升级到BCrypt加密方式的?

我们知道spring security里面默认使用的PasswordEncoder是这样的。

@Bean
public PasswordEncoder passwordEncoder() {
    return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}

不知道当做知道哈

他们内部的源码是这样的。

public static PasswordEncoder createDelegatingPasswordEncoder() {
    // 省略了一堆代码
   String encodingId = "bcrypt";
   Map<String, PasswordEncoder> encoders = new HashMap<>();
   encoders.put(encodingId, new BCryptPasswordEncoder());
   encoders.put("MD5", new org.springframework.security.crypto.password.MessageDigestPasswordEncoder("MD5"));
   encoders.put("noop", org.springframework.security.crypto.password.NoOpPasswordEncoder.getInstance());
   return new DelegatingPasswordEncoder(encodingId, encoders);
}

嗯,你要注意这几行代码。

String encodingId = "bcrypt";
encoders.put(encodingId, new BCryptPasswordEncoder());
return new DelegatingPasswordEncoder(encodingId, encoders);

别的什么都不看,只看encodingId变量。我们现在进入DelegatingPasswordEncoder的内部看看他的构造函数。

public DelegatingPasswordEncoder(String idForEncode, Map<String, PasswordEncoder> idToPasswordEncoder,
      String idPrefix, String idSuffix) {
    // 省略一堆代码
   this.idForEncode = idForEncode;
   this.passwordEncoderForEncode = idToPasswordEncoder.get(idForEncode);
   this.idToPasswordEncoder = new HashMap<>(idToPasswordEncoder);
   this.idPrefix = idPrefix;
   this.idSuffix = idSuffix;
}

encodingId在这个类中被叫做idForEncode

了解了这个之后,再关注这几行代码。

this.idForEncode = idForEncode;
this.passwordEncoderForEncode = idToPasswordEncoder.get(idForEncode);

是不是相当于

this.idForEncode = "bcrypt";
this.passwordEncoderForEncode = new BCryptPasswordEncoder();

我们再回到这里看红框框的这行代码。

@Override
public String encode(CharSequence rawPassword) {
   return this.idPrefix + this.idForEncode + this.idSuffix + this.passwordEncoderForEncode.encode(rawPassword);
}

现在你对比一下这个函数跟前面构造函数的名字看看。

构造函数的变量叫 idForEncode , encode函数也叫 idForEncode , 前面的构造函数,我们发现这个变量其实已经被保存在DelegatingPasswordEncoder类里面了。而且值还是"bcrypt"

而构造函数里面this.passwordEncoderForEncode = idToPasswordEncoder.get(idForEncode)

idToPasswordEncoder就是个Map, k是每种加密对象的id, v是每种加密算法

比如: key = "bcrypt", 那么 value = "BCryptPasswordEncoder"

所以 idToPasswordEncoderencode 函数时, 是BCryptPasswordEncoder

小白: "什么玩意儿, 乱七八糟的, 看不懂"

小黑: "抱歉表达能力不行, 我简单点说"

小黑: "因为PasswordEncoderFactories.createDelegatingPasswordEncoder()函数使用bcrypt作为默认加密方式, 所以在调用PasswordEncoder.encode时默认也使用bcrypt"

小黑: "还不懂就配合下面的图片看看"

造成它默认是BCryptPasswordEncoder的原因是什么?

就上面这一行代码

搞懂这个有什么作用呢?

Spring security默认全部加密方式升级方案全部都是bcrypt,那如果我们要自定义升级到我们需要的加密方式呢?

重写PasswordEncoderFactories类, 把上面的变量修改成你需要修改的加密类型, 并且往Map中添加加密类型的对象

public static PasswordEncoder createDelegatingPasswordEncoder() {
   String encodingId = "无敌加密";
   Map<String, PasswordEncoder> encoders = new HashMap<>();
   encoders.put(encodingId, new 无敌加密PasswordEncoder());
    // 省略一堆代码
   return new DelegatingPasswordEncoder(encodingId, encoders);
}

我去跑题了, 回归正题

第二种方式: 多继承接口方式

public class UserService implements UserDetailsService, UserDetailsPasswordService {
   @Resource
   private UsersMapper usersMapper;
   /**
    * 升级用户密码为当前加密方式
    *
    * @param user        要修改的用户, 这个用户必须有 id
    * @param newPassword 新的密码, 该密码已经被 passwordEncoder 加密
    * @return
    */
   @Override
   public UserDetails updatePassword(UserDetails user, String newPassword) {
      if (user instanceof Users users) {
         users.setPassword(newPassword);
         usersMapper.updateByPrimaryKeySelective(users);
      }
      return user;
   }
   @Override
   public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
      Optional<Users> optionalUsers = Optional.ofNullable(usersMapper.loadUserByUsername(username));
      return optionalUsers.orElseThrow(() -> new UsernameNotFoundException("找不到用户"));
   }
}

这种方式对应着上面三张图片的第1张图片给出的方案

第三种方式: HttpSecurity直接添加

@Bean
public AuthenticationManager authenticationManager(HttpSecurity http) throws Exception {
   AuthenticationManagerBuilder authenticationManagerBuilder = http.getSharedObject(AuthenticationManagerBuilder.class);
   authenticationManagerBuilder.authenticationProvider(/* 你的认证七 */)
         .userDetailsService(/* 加载用户方式 */)
         .passwordEncoder(/* 密码加密方式 */)
         .userDetailsPasswordManager(/* 第三种更新加密的方式 */);
   return authenticationManagerBuilder.build();
}

这种方式比较麻烦, 只有你需要重写某个Provider的时候才会用到

一般我们使用第二种方式就行

以上就是Spring Security如何实现升级密码加密方式详解的详细内容,更多关于Spring Security升级密码加密的资料请关注我们其它相关文章!

(0)

相关推荐

  • Idea中如何查看SpringSecurity各Filter信息

    目录 Filter和Filter Chain Idea Evalute Expression 创建工程 debug启动服务 evaluate expression 总结 Filter和Filter Chain SpringSecurity的认证逻辑是通过Filter Chain实现的,一个项目中Filter是链式执行,其中一环校验不通过,则可终止后续Filter以及Api的调用.     public void doFilter(ServletRequest request, ServletRe

  • SpringSecurity自定义Form表单使用方法讲解

    目录 背景 实验-HttpBasic 实验-自定义登录页面 实验-自定义登录接口 实验-自定义登录数据参数 实验-自定义登录失败.成功处理器 实验-自定义登录成功跳转页面 实验-自定义退出接口 背景 本系列教程,是作为团队内部的培训资料准备的.主要以实验的方式来体验SpringSecurity的各项Feature. 新建一个SpringBoot项目,起名springboot-security-form,核心依赖为Web,SpringSecurity与Thymeleaf. <dependencie

  • 详解Spring Security怎么从数据库加载我们的用户

    目录 本章内容 如何从数据库中读取用户对象? 脱敏为什么没有生效? 总结 本章内容 如何从数据库中读取用户对象 源码分析 如何从数据库中读取用户对象? 1前面我们分析认证的时候就会发现他在DaoAuthenticationProvider中就已经有从数据库中获取用户名和密码的. public interface UserDetailsService { UserDetails loadUserByUsername(String username) throws UsernameNotFoundE

  • SpringSecurity导致SpringBoot跨域失效的问题解决

    目录 1.CORS 是什么 2.预检请求 3.三种配置的方式 3.1 @CrossOrigin 注解 3.2 实现 WebMvcConfigurer.addCorsMappings 方法 3.3 注入 CorsFilter 4.Spring Security 中的配置 5.这些配置有什么区别 5.1 Filter 与 Interceptor 5.2 WebMvcConfigurer.addCorsMappings 方法做了什么 5.2.1 注入 CORS 配置 5.2.2 获取 CORS 配置

  • SpringBoot Security实现单点登出并清除所有token

    目录 需求 记录token 清除token 解决登出时长过长 需求 A.B.C 系统通过 sso 服务实现登录 A.B.C 系统分别获取 Atoken.Btoken.Ctoken 三个 token 其中某一个系统主动登出后,其他两个系统也登出 至此全部 Atoken.Btoken.Ctoken 失效 记录token pom 文件引入依赖 Redis数据库依赖 hutool:用于解析token <dependency> <groupId>org.springframework.boo

  • Spring Security实现添加图片验证功能

    目录 本章内容 思路 方案 怎么将字符串变成图片验证码? kaptcha这么玩 hutool这么玩 传统web项目 过滤器方式 认证器方式 总结下 前后端分离项目 基于过滤器方式 基于认证器方式 本章内容 Spring security添加图片验证方式,在互联网上面有很多这种博客,都写的非常的详细了.本篇主要讲一些添加图片验证的思路.还有前后端分离方式,图片验证要怎么去处理? 图片验证的思路 简单的demo 思路 小白: "我们从总体流程上看图片验证在认证的哪一个阶段?" 小黑: &q

  • Spring Security如何实现升级密码加密方式详解

    目录 本章内容 密码加密方式怎么升级? 升级方案源码 实战 第一种方式: Spring Bean 他是怎么自动升级到BCrypt加密方式的? 第二种方式: 多继承接口方式 第三种方式: HttpSecurity直接添加 本章内容 密码加密方式怎么升级? spring security底层怎么实现的密码加密方式升级? 密码加密方式怎么升级? 前面我们学过DelegatingPasswordEncoder类,但是不清楚他到底是做什么的,我也没讲的很清楚.所以呢,我们就重新再讲一讲它的另一个实际应用.

  • Java使用bcrypt实现对密码加密效果详解

    目录 简介 bcrypt加密原理 加密过程 校验过程 bcrypt与md5的区别 示例 1.引入依赖 2.写测试类 3.测试 密文含义 简介 本文用示例介绍使用对密码进行加密的算法:bcrypt. bcrypt是一种自带盐值(自动加盐)的加密方案. bcrypt加密原理 加密过程 先随机生成salt salt跟password进行hash 注意 对于同一个密码,每次生成的hash是不同的 hash中包含了salt 校验过程 从hash中取出salt salt跟password进行hash计算 将

  • Spring Security实现接口放通的方法详解

    目录 1.SpringBoot版本 2.实现思路 3.实现过程 3.1新建注解 3.2新建请求枚举类 3.3判断Controller方法上是否存在该注解 3.4在SecurityConfig上进行策略的配置 3.5在Controller方法上应用 3.6效果展示 在用Spring Security项目开发中,有时候需要放通某一个接口时,我们需要在配置中把接口地址配置上,这样做有时候显得麻烦,而且不够优雅.我们能不能通过一个注解的方式,在需要放通的接口上加上该注解,这样接口就能放通了.答案肯定是可

  • 微信小程序 sha1 实现密码加密实例详解

    微信小程序 sha1 实现密码加密实例详解 在utils中的util.js 文件中增加 函数 实现 字符串转换为16进制加密后的字符串 function encodeUTF8(s) { var i, r = [], c, x; for (i = 0; i < s.length; i++) if ((c = s.charCodeAt(i)) < 0x80) r.push(c); else if (c < 0x800) r.push(0xC0 + (c >> 6 & 0x

  • Spring Data Jpa的四种查询方式详解

    这篇文章主要介绍了Spring Data Jpa的四种查询方式详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 一.调用接口的方式 1.基本介绍 通过调用接口里的方法查询,需要我们自定义的接口继承Spring Data Jpa规定的接口 public interface UserDao extends JpaRepository<User, Integer>, JpaSpecificationExecutor<User> 使用这

  • Spring Bean属性注入的两种方式详解

    目录 属性注入概念 一.构造器注入 示例1 注意点 二.setter注入 示例2 三.如何选择注入方式 属性注入概念 Spring 属性注入(DI依赖注入)有两种方式:setter注入,构造器注入. 这个注入的属性可以是普通属性(基本数据类型与String等),也可以是一个引用数据类型(主要是对象),或者是一个集合(list.map.set等) 下表是属性注入bean标签中常用的元素 元素名称 描述 constructor-arg 构造器注入.该元素的 index 属性指定构造参数的索引(从 0

  • Spring中Bean的三种实例化方式详解

    目录 一.环境准备 二.构造方法实例化 三.分析Spring的错误信息 四.静态工厂实例化 4.1 工厂方式创建bean 4.2 静态工厂实例化 五.实例工厂与FactoryBean 5.1 环境准备 5.2 实例工厂实例化 5.3 FactoryBean的使用 六.bean实例化小结 一.环境准备 准备开发环境 创建一个Maven项目 pom.xml添加依赖 resources下添加spring的配置文件applicationContext.xml 最终项目的结构如下: 二.构造方法实例化 在

  • Spring框架实现AOP的两种方式详解

    目录 第一种AOP实现方式 AfterLog Log 配置文件 实例调用 定义接口 第二种AOP实现方式 第一种AOP实现方式 AfterLog package com.xxx.demo.service1; import org.junit.After; import org.springframework.aop.AfterReturningAdvice; import java.lang.reflect.Method; public class AfterLog implements Aft

  • SpringBoot + Spring Security 基本使用及个性化登录配置详解

    Spring Security 基本介绍 这里就不对Spring Security进行过多的介绍了,具体的可以参考官方文档 我就只说下SpringSecurity核心功能: 认证(你是谁) 授权(你能干什么) 攻击防护(防止伪造身份) 基本环境搭建 这里我们以SpringBoot作为项目的基本框架,我这里使用的是maven的方式来进行的包管理,所以这里先给出集成Spring Security的方式 <dependencies> ... <dependency> <groupI

  • Python实现最常见加密方式详解

    前言 我们所说的加密方式,都是对二进制编码的格式进行加密的,对应到Python中,则是我们的Bytes. 所以当我们在Python中进行加密操作的时候,要确保我们操作的是Bytes,否则就会报错. 将字符串和Bytes互相转换可以使用encode()和decode()方法.如下所示: # 方法中不传参数则是以默认的utf-8编码进行转换In [1]: '南北'.encode()Out[1]: b'\xe5\x8d\x97\xe5\x8c\x97'In [2]: b'\xe5\x8d\x97\xe

随机推荐