专栏首页用户7614879的专栏Spring security 同时支持多种加密方式

Spring security 同时支持多种加密方式

参考文章:http://www.javaboy.org/2020/0618/passwordencoder.html

spring security一系列都是从这里学的 非常感谢作者


在security框架中 如果没有创建任何一个PasswordEncoder 则会使用默认的 ,如果在容器中有PasswordEncoder 则会使用容器中的,源码如下:

@Order(InitializeUserDetailsBeanManagerConfigurer.DEFAULT_ORDER)
class InitializeUserDetailsBeanManagerConfigurer extends GlobalAuthenticationConfigurerAdapter {

	static final int DEFAULT_ORDER = Ordered.LOWEST_PRECEDENCE - 5000;

	private final ApplicationContext context;

	/**
	 * @param context
	 */
	InitializeUserDetailsBeanManagerConfigurer(ApplicationContext context) {
		this.context = context;
	}

	@Override
	public void init(AuthenticationManagerBuilder auth) throws Exception {
		auth.apply(new InitializeUserDetailsManagerConfigurer());
	}

	class InitializeUserDetailsManagerConfigurer extends GlobalAuthenticationConfigurerAdapter {

		@Override
		public void configure(AuthenticationManagerBuilder auth) throws Exception {
			if (auth.isConfigured()) {
				return;
			}
			UserDetailsService userDetailsService = getBeanOrNull(UserDetailsService.class);
			if (userDetailsService == null) {
				return;
			}
            //从容器中获取 没有则会返回null
			PasswordEncoder passwordEncoder = getBeanOrNull(PasswordEncoder.class);
			UserDetailsPasswordService passwordManager = getBeanOrNull(UserDetailsPasswordService.class);
            //Provider的构造方法中 设置了默认的
			DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
			provider.setUserDetailsService(userDetailsService);
            //如果不是null 则使用容器中获取的
			if (passwordEncoder != null) {
				provider.setPasswordEncoder(passwordEncoder);
			}
			if (passwordManager != null) {
				provider.setUserDetailsPasswordService(passwordManager);
			}
			provider.afterPropertiesSet();
			auth.authenticationProvider(provider);
		}

		/**
		 * @return a bean of the requested class if there's just a single registered
		 * component, null otherwise.
		 */
		private <T> T getBeanOrNull(Class<T> type) {
			String[] beanNames = InitializeUserDetailsBeanManagerConfigurer.this.context.getBeanNamesForType(type);
			if (beanNames.length != 1) {
				return null;
			}
			return InitializeUserDetailsBeanManagerConfigurer.this.context.getBean(beanNames[0], type);
		}

	}

}

而默认的encoder是一个同时支持多种加密方式的代理对象 provider的构造方法如下:

	public DaoAuthenticationProvider() {
		setPasswordEncoder(PasswordEncoderFactories.createDelegatingPasswordEncoder());
	}

里面的具体实现是

	@SuppressWarnings("deprecation")
	public static PasswordEncoder createDelegatingPasswordEncoder() {
		String encodingId = "bcrypt";
		Map<String, PasswordEncoder> encoders = new HashMap<>();
		encoders.put(encodingId, new BCryptPasswordEncoder());
		encoders.put("ldap", new org.springframework.security.crypto.password.LdapShaPasswordEncoder());
		encoders.put("MD4", new org.springframework.security.crypto.password.Md4PasswordEncoder());
		encoders.put("MD5", new org.springframework.security.crypto.password.MessageDigestPasswordEncoder("MD5"));
		encoders.put("noop", org.springframework.security.crypto.password.NoOpPasswordEncoder.getInstance());
		encoders.put("pbkdf2", new Pbkdf2PasswordEncoder());
		encoders.put("scrypt", new SCryptPasswordEncoder());
		encoders.put("SHA-1", new org.springframework.security.crypto.password.MessageDigestPasswordEncoder("SHA-1"));
		encoders.put("SHA-256",
				new org.springframework.security.crypto.password.MessageDigestPasswordEncoder("SHA-256"));
		encoders.put("sha256", new org.springframework.security.crypto.password.StandardPasswordEncoder());
		encoders.put("argon2", new Argon2PasswordEncoder());
		return new DelegatingPasswordEncoder(encodingId, encoders);
	}

可以看到 生成了一个DelegatingPasswordEncoder对象 使用的ID是bcrypt 也就代表了该encoder 加密时使用的是bcrypt,解密时可以根据密码所带前缀选择不同的解密方式,如果没有带加密前缀,则会使用默认的default方式。

	private PasswordEncoder defaultPasswordEncoderForMatches = new UnmappedIdPasswordEncoder();




	private class UnmappedIdPasswordEncoder implements PasswordEncoder {

		@Override
		public String encode(CharSequence rawPassword) {
			throw new UnsupportedOperationException("encode is not supported");
		}

		@Override
		public boolean matches(CharSequence rawPassword, String prefixEncodedPassword) {
			String id = extractId(prefixEncodedPassword);
			throw new IllegalArgumentException("There is no PasswordEncoder mapped for the id \"" + id + "\"");
		}

	}

可以看到,默认都会直接抛出异常。

前缀带密码是这样的:

{bcrypt}$2a$10$Sb1gAUH4wwazfNiqflKZve4Ubh.spJcxgHG8Cp29DeGya5zsHENqi
{MD5}{Wucj/L8wMTMzFi3oBKWsETNeXbMFaHZW9vCK9mahMHc=}4d43db282b36d7f0421498fdc693f2a2
{noop}123

所以如果需要同时支持多种解密方式,只需要将原本的map扩展一下,使其支持自己写的encoder,并且将默认encoder的设置成你想要的,作为没有前缀时的解密方式即可。

当然直接使用自己写的encoder 在其中加入各种判断也是可以的。看个人选择。

@Bean
public PasswordEncoder passwordEncoder() {
    return MyPasswordEncoderFactories.createDelegatingPasswordEncoder();
}
class MyPasswordEncoderFactories {
 
    @SuppressWarnings("deprecation")
    static PasswordEncoder createDelegatingPasswordEncoder() {
        String encodingId = "bcrypt";
        Map<String, PasswordEncoder> encoders = new HashMap<>();
        encoders.put(encodingId, new BCryptPasswordEncoder());
        encoders.put("ldap", new org.springframework.security.crypto.password.LdapShaPasswordEncoder());
        encoders.put("MD4", new org.springframework.security.crypto.password.Md4PasswordEncoder());
        encoders.put("MD5", new org.springframework.security.crypto.password.MessageDigestPasswordEncoder("MD5"));
        encoders.put("noop", org.springframework.security.crypto.password.NoOpPasswordEncoder.getInstance());
        encoders.put("pbkdf2", new Pbkdf2PasswordEncoder());
        encoders.put("scrypt", new SCryptPasswordEncoder());
        encoders.put("SHA-1", new org.springframework.security.crypto.password.MessageDigestPasswordEncoder("SHA-1"));
        encoders.put("SHA-256", new org.springframework.security.crypto.password.MessageDigestPasswordEncoder("SHA-256"));
        //加入自定义前缀
        encoders.put("SM3",new MyPasswordEncoder());
        DelegatingPasswordEncoder delegatingPasswordEncoder = new DelegatingPasswordEncoder(encodingId, encoders);
        //指定没有前缀时使用的方式
        delegatingPasswordEncoder.setDefaultPasswordEncoderForMatches(new MyPasswordEncoder());
        return delegatingPasswordEncoder;
    }
 
}

这样就可以使用多种加密方式了。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Spring Security灵活的PasswordEncoder加密方式

    本章基于Spring Security 5.4.1版本编写,从5.x版本开始引入了很多新的特性。 为了适配老系统的安全框架升级,Spring Security也...

    恒宇少年
  • OAuth2 升级Spring Cloud Finchley.RELEASE踩坑分享

    6.19号,spring团队发布了期待已久的 Spring Cloud Finchley.RELEASE 版本。 重要变化:

    冷冷
  • Spring Security与Java应用安全保护

    Spring Security是一个强大且高度可定制的安全框架,致力于为Java应用提供身份认证和授权。

    博文视点Broadview
  • 宇智波程序笔记10-为什么你使用的 Spring Security OAuth 过期了

    那么到底选择哪一个依赖合适呢?这不同的依赖又有什么区别?今天松哥就来和大家聊一聊 Spring Security 中关于 OAuth2 的恩怨。

    不会飞的小鸟
  • 为什么你使用的 Spring Security OAuth 过期了?松哥来和大家捋一捋!

    松哥原创的 Spring Boot 视频教程已经杀青,感兴趣的小伙伴戳这里-->Spring Boot+Vue+微人事视频教程

    江南一点雨
  • 【SpringSecurity系列(一)】初识 Spring Security

    《深入浅出Spring Security》一书已由清华大学出版社正式出版发行,感兴趣的小伙伴戳这里->->>深入浅出Spring Security,一本书学会 ...

    江南一点雨
  • Light Security 1.0.1发布

    Light Security是一款简洁而不简单的权限控制框架,基于 jwt ,支持与 Spring Boot 配合使用。

    用户1516716
  • 挖一个大坑,Spring Security 开搞!

    自从 Spring Boot、Spring Cloud 火起来之后,Spring Security 也跟着沾了一把光!

    江南一点雨
  • 在微服务项目中,Spring Security 比 Shiro 强在哪?

    虽然目前 Spring Security 一片火热,但是 Shiro 的市场依然存在,今天我就来稍微的说一说这两个框架的,方便大家在实际项目中选择适合自己的安全...

    江南一点雨
  • Api架构奥义:ApiBoot实现零代码整合Spring Security & OAuth2

    接口服务的安全性一直是程序员比较注重的一个问题,成熟的安全框架也比较多,其中一个组合就是Spring Security与OAuth2的整合,在ApiBoot内通...

    恒宇少年
  • Spring Security入门到实践(一)HTTP Basic在Spring Security中的应用原理浅析

    上面的两点是应用安全的基本关注点,Spring Security存在的意义就是帮助开发者更加便捷地实现了应用的认证和授权能力。

    itlemon
  • 手把手带你入门 Spring Security!

    Spring Security 是 Spring 家族中的一个安全管理框架,实际上,在 Spring Boot 出现之前,Spring Security 就已经...

    江南一点雨
  • Spring Boot2 系列教程(三十三)整合 Spring Security

    Spring Security 是 Spring 家族中的一个安全管理框架,实际上,在 Spring Boot 出现之前,Spring Security 就已经...

    江南一点雨
  • Spring Security 多种加密方案共存,老破旧系统整合利器!

    松哥给最近连载的 Spring Security 系列也录制了视频教程,感兴趣的小伙伴请戳这里->Spring Boot+Vue+微人事视频教程(Spring ...

    江南一点雨
  • 在 SpringBoot 项目中,Spring Security 和 Shiro 该如何选择?

    要知道Shiro和Spring Security该如何选择,首先要看看两者的区别和对比

    好好学java
  • Spring Boot 中如何实现 HTTP 认证?

    松哥给最近连载的 Spring Security 系列也录制了视频教程,感兴趣的小伙伴请戳这里->Spring Boot+Vue+微人事视频教程(Spring ...

    江南一点雨
  • oAuth2 升级Spring Cloud Finchley.RELEASE踩坑分享 .md

    6.19号,spring团队发布了期待已久的 Spring Cloud Finchley.RELEASE 版本。

    冷冷
  • 安全框架 Shiro 和 Spring Security 如何选择?

    安全框架,简单说是对访问权限进行控制,应用的安全性包括用户认证(Authentication)和用户授权(Authorization)两个部分。

    良月柒
  • 在 SpringBoot 项目中,Spring Security 和 Shiro 该如何选择?

    要知道Shiro和Spring Security该如何选择,首先要看看两者的区别和对比

    芋道源码

扫码关注云+社区

领取腾讯云代金券