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

 更新时间:2023年01月04日 11:45:48   作者:bangiao  
这篇文章主要为大家介绍了Spring Security实现升级密码加密方式详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

本章内容

  • 密码加密方式怎么升级?
  • 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升级密码加密的资料请关注脚本之家其它相关文章!

相关文章

  • java去除数组重复元素的四种方法

    java去除数组重复元素的四种方法

    本文给大家分享四种java去除数组重复元素的方法,每种方法通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧
    2021-11-11
  • 深度解析Java中ArrayList的使用

    深度解析Java中ArrayList的使用

    ArrayList 类是一个可以动态修改的数组,与普通数组的区别就是它是没有固定大小的限制,我们可以添加或删除元素。本文将通过示例带你深度解析Java中ArrayList的使用,需要的可以参考一下
    2022-09-09
  • 解决SpringMvc后台接收json数据中文乱码问题的几种方法

    解决SpringMvc后台接收json数据中文乱码问题的几种方法

    本篇文章主要介绍了解决SpringMvc后台接收json数据中文乱码问题的几种方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-01-01
  • Java字符串转驼峰格式的方法

    Java字符串转驼峰格式的方法

    在开发场景中,我们会遇到一些涉及字符串的转化,本文主要介绍了Java字符串转驼峰格式的方法,具有一定的参考价值,感兴趣的可以了解一下
    2024-01-01
  • Windows10安装IDEA 2020.1.2的方法步骤

    Windows10安装IDEA 2020.1.2的方法步骤

    这篇文章主要介绍了Windows10安装IDEA 2020.1.2的方法步骤,文中通过图文介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-08-08
  • 浅谈JDK8中的Duration Period和ChronoUnit

    浅谈JDK8中的Duration Period和ChronoUnit

    在JDK8中,引入了三个非常有用的时间相关的API:Duration,Period和ChronoUnit。他们都是用来对时间进行统计的,本文将会详细讲解一下这三个API的使用
    2021-06-06
  • Java超详细整理讲解各种排序

    Java超详细整理讲解各种排序

    这篇文章主要介绍了Java常用的排序算法及代码实现,在Java开发中,对排序的应用需要熟练的掌握,这样才能够确保Java学习时候能够有扎实的基础能力。那Java有哪些排序算法呢?本文小编就来详细说说Java常见的排序算法,需要的朋友可以参考一下
    2022-07-07
  • WebSocket无法注入属性的问题及解决方案

    WebSocket无法注入属性的问题及解决方案

    这篇文章主要介绍了WebSocket无法注入属性的问题及解决方法,本文通过示例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-09-09
  • 如何解决LocalDateTime传值JSON格式化问题

    如何解决LocalDateTime传值JSON格式化问题

    这篇文章主要介绍了如何解决LocalDateTime传值JSON格式化问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-08-08
  • Spring AOP注解实战指南

    Spring AOP注解实战指南

    在现代软件开发中,面向切面编程(AOP)是一种强大的编程范式,本文将介绍如何在Spring框架中通过AspectJ注解以及对应的XML配置来实现AOP,在不改变主业务逻辑的情况下增强应用程序的功能,需要的朋友可以参考下
    2024-06-06

最新评论