当前位置: 首页 > news >正文

2016网站开发语言/seo网站优化培训价格

2016网站开发语言,seo网站优化培训价格,亚马逊一个月赚5万难吗,美食网站开发背景Spring Security中的密码安全 PasswordEncoder 接口 在 Spring Security 中,PasswordEncoder 接口代表的是一种密码编码器,其核心作用在于指定密码的具体加密方式,以及如何将一段给定的加密字符串与明文之间完成匹配校验。PasswordEncoder 接…

Spring Security中的密码安全

PasswordEncoder 接口

在 Spring Security 中,PasswordEncoder 接口代表的是一种密码编码器,其核心作用在于指定密码的具体加密方式,以及如何将一段给定的加密字符串与明文之间完成匹配校验。PasswordEncoder 接口定义如下:

public interface PasswordEncoder {//对原始密码进行编码String encode(CharSequence rawPassword);//对提交的原始密码与库中存储的加密密码进行比对boolean matches(CharSequence rawPassword, String encodedPassword);//判断加密密码是否需要再次进行加密,默认返回 falsedefault boolean upgradeEncoding(String encodedPassword) {return false;}
}

Spring Security 内置了一大批 PasswordEncoder 接口的实现类,如下所示:

image-20210814175248599

上图中比较常见的几个 PasswordEncoder 接口展开叙述。

  • NoOpPasswordEncoder:以明文形式保留密码,不对密码进行编码。这种 PasswordEncoder 通常只用于演示,不应该用于生产环境。
  • StandardPasswordEncoder:使用 SHA-256 算法对密码执行哈希操作。
  • BCryptPasswordEncoder:使用 bcrypt 强哈希算法对密码执行哈希操作。
  • Pbkdf2PasswordEncoder:使用 PBKDF2 算法对密码执行哈希操作。

下面以 BCryptPasswordEncoder 为例,看一下它的 encode 方法,如下所示:

public String encode(CharSequence rawPassword) {String salt;if (random != null) {salt = BCrypt.gensalt(version.getVersion(), strength, random);} else {salt = BCrypt.gensalt(version.getVersion(), strength);}return BCrypt.hashpw(rawPassword.toString(), salt);
}

可以看到,上述 encode 方法执行了两个步骤,首先使用 Spring Security 提供的 BCrypt 工具类生成盐(Salt),然后根据盐和明文密码生成最终的密文密码。这里有必要对加盐的概念做一些展开:所谓加盐,就是在初始化明文数据时,由系统自动往这个明文里添加一些附加数据,然后散列。引入加盐机制是为了进一步保证加密数据的安全性,单向散列加密以及加盐思想也被广泛应用于系统登录过程中的密码生成和校验。

同样,在 Pbkdf2PasswordEncoder 中,也是通过对密码加盐之后进行哈希,然后将结果作为盐再与密码进行哈希,多次重复此过程,生成最终的密文。

续来看它的应用方式。如果我们想在应用程序中使用某一个 PasswordEncoder 实现类,通常只需要通过它的构造函数创建一个实例,例如:

PasswordEncoder p = new StandardPasswordEncoder(); 
PasswordEncoder p = new StandardPasswordEncoder("secret");PasswordEncoder p = new SCryptPasswordEncoder(); 
PasswordEncoder p = new SCryptPasswordEncoder(16384, 8, 1, 32, 64);

而如果想要使用 NoOpPasswordEncoder,除了构造函数之外,还可以通过它的 getInstance() 方法来获取静态实例,如下所示:

PasswordEncoder p = NoOpPasswordEncoder.getInstance()

自定义 PasswordEncoder

尽管 Spring Security 已经为我们提供了丰富的 PasswordEncoder,但你也可以通过实现这个接口来设计满足自身需求的任意一种密码编解码和验证机制。例如,我们可以编写如下所示的一个 PlainTextPasswordEncoder:

public class PlainTextPasswordEncoder implements PasswordEncoder {@Overridepublic String encode(CharSequence rawPassword) {return rawPassword.toString(); }@Overridepublic boolean matches(CharSequence rawPassword, String encodedPassword) {return rawPassword.equals(encodedPassword); }
}

PlainTextPasswordEncoder 的功能与 NoOpPasswordEncoder 类似,没有对明文进行任何处理。如果你想使用某种算法集成 PasswordEncoder,就可以实现类似如下所示的 Sha512PasswordEncoder,这里使用了 SHA-512 作为加解密算法:

public class Sha512PasswordEncoder implements PasswordEncoder {@Overridepublic String encode(CharSequence rawPassword) {return hashWithSHA512(rawPassword.toString());}@Overridepublic boolean matches(CharSequence rawPassword, String encodedPassword) {String hashedPassword = encode(rawPassword);return encodedPassword.equals(hashedPassword);}private String hashWithSHA512(String input) {StringBuilder result = new StringBuilder();try {MessageDigest md = MessageDigest.getInstance("SHA-512");byte [] digested = md.digest(input.getBytes());for (int i = 0; i < digested.length; i++) {result.append(Integer.toHexString(0xFF & digested[i]));}} catch (NoSuchAlgorithmException e) {throw new RuntimeException("Bad algorithm");}return result.toString();}
}

上述代码中,hashWithSHA512() 方法就使用了前面提到的单向散列加密算法来生成消息摘要(Message Digest),其主要特点在于单向不可逆和密文长度固定。同时也具备“碰撞”少的优点,即明文的微小差异就会导致所生成密文完全不同。SHA(Secure Hash Algorithm)以及MD5(Message Digest 5)都是常见的单向散列加密算法,在 JDK 自带的 MessageDigest 类中已经包含了默认实现,我们直接调用方法即可。

代理式 DelegatingPasswordEncoder

在前面的讨论中,我们都基于一个假设,即在对密码进行加解密过程中,只会使用到一个 PasswordEncoder,如果这个 PasswordEncoder 不满足我们的需求,那么就需要替换成另一个 PasswordEncoder。这就引出了一个问题,如何优雅地应对这种变化呢?

在普通的业务系统中,由于业务系统也在不断地变化,替换一个组件可能并没有很高的成本。但对于 Spring Security 这种成熟的开发框架而言,在设计和实现上不能经常发生变化。因此,在新/旧 PasswordEncoder 的兼容性,以及框架自身的稳健性和可变性之间需要保持一种平衡。为了实现这种平衡性,Spring Security 提供了 DelegatingPasswordEncoder。

虽然 DelegatingPasswordEncoder 也实现了 PasswordEncoder 接口,但事实上,它更多扮演了一种代理组件的角色,这点从命名上也可以看出来。DelegatingPasswordEncoder 将具体编码的实现根据要求代理给不同的算法,以此实现不同编码算法之间的兼容并协调变化,如下图所示:

下面我们来看一下 DelegatingPasswordEncoder 类的构造函数,如下所示:

public DelegatingPasswordEncoder(String idForEncode,Map<String, PasswordEncoder> idToPasswordEncoder) {if (idForEncode == null) {throw new IllegalArgumentException("idForEncode cannot be null");}if (!idToPasswordEncoder.containsKey(idForEncode)) {throw new IllegalArgumentException("idForEncode " + idForEncode + "is not found in idToPasswordEncoder " + idToPasswordEncoder);}for (String id : idToPasswordEncoder.keySet()) {if (id == null) {continue;}if (id.contains(PREFIX)) {throw new IllegalArgumentException("id " + id + " cannot contain " + PREFIX);}if (id.contains(SUFFIX)) {throw new IllegalArgumentException("id " + id + " cannot contain " + SUFFIX);}}this.idForEncode = idForEncode;this.passwordEncoderForEncode = idToPasswordEncoder.get(idForEncode);this.idToPasswordEncoder = new HashMap<>(idToPasswordEncoder);
}

该构造函数中的 idForEncode 参数决定 PasswordEncoder 的类型,而 idToPasswordEncoder 参数决定判断匹配时兼容的类型。显然,idToPasswordEncoder 必须包含对应的 idForEncode。

我们再来看这个构造函数的调用入口。在 Spring Security 中,存在一个创建 PasswordEncoder 的工厂类 PasswordEncoderFactories,如下所示:

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);}

可以看到,在这个工厂类中初始化了一个包含所有 Spring Security 中支持 PasswordEncoder 的 Map。而且,我们也明确了框架默认使用的就是 key 为“bcrypt”的 BCryptPasswordEncoder。

通常,我们可以通过以下方法来使用这个 PasswordEncoderFactories 类:

PasswordEncoder passwordEncoder =PasswordEncoderFactories.createDelegatingPasswordEncoder();

另一方面,PasswordEncoderFactories 的实现方法为我们自定义 DelegatingPasswordEncoder 提供了一种途径,我们也可以根据需要创建符合自己需求的 DelegatingPasswordEncoder,如下所示:

String idForEncode = "bcrypt";
Map encoders = new HashMap<>();
encoders.put(idForEncode, new BCryptPasswordEncoder());
encoders.put("noop", NoOpPasswordEncoder.getInstance());
encoders.put("pbkdf2", new Pbkdf2PasswordEncoder());
encoders.put("scrypt", new SCryptPasswordEncoder());
encoders.put("sha256", new StandardPasswordEncoder());PasswordEncoder passwordEncoder =new DelegatingPasswordEncoder(idForEncode, encoders);

这里的 id 就是 PasswordEncoder 的种类,也就是前面提到的 idForEncode 参数。假设密码原文为“password”,经过 BCryptPasswordEncoder 进行加密之后的密文就变成了这样一个字符串:

$2a$10$dXJ3SW6G7P50lGmMkkmwe.20cQQubK3.HZWzG3YB1tlRy.fqvM/BG

最终存储在数据库中的密文应该是这样的:

{bcrypt}$2a$10$dXJ3SW6G7P50lGmMkkmwe.20cQQubK3.HZWzG3YB1tlRy.fqvM/BG

以上实现过程可以通过查阅 DelegatingPasswordEncoder 的 encode() 方法得到验证:

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

测试结果如下:

public static void main(String[] args) {PasswordEncoder delegatingPasswordEncoder = PasswordEncoderFactories.createDelegatingPasswordEncoder();//{bcrypt}$2a$10$a8P9wnazhvUTv8g/FMQgluSh1kjps033h0rdCKcVluhi90mvdoRqOSystem.out.println(delegatingPasswordEncoder.encode("123"));String idForEncode = "bcrypt";Map encoders = new HashMap<>();encoders.put(idForEncode, new BCryptPasswordEncoder());encoders.put("noop", NoOpPasswordEncoder.getInstance());encoders.put("pbkdf2", new Pbkdf2PasswordEncoder());encoders.put("scrypt", new SCryptPasswordEncoder());encoders.put("sha256", new StandardPasswordEncoder());PasswordEncoder passwordEncoder =new DelegatingPasswordEncoder("sha256", encoders);//{sha256}fd9d463223519d6d993eb684fa2ef83d068b4826bc9d099224ccba4533fa9f1eac3dda95a9c507faSystem.out.println(passwordEncoder.encode("123"));}

我们继续来看 DelegatingPasswordEncoder 的 matcher 方法,如下所示:

@Override
public boolean matches(CharSequence rawPassword, String prefixEncodedPassword) {if (rawPassword == null && prefixEncodedPassword == null) {return true;}//取出 PasswordEncoder 的 idString id = extractId(prefixEncodedPassword);//根据 PasswordEncoder 的 id 获取对应的 PasswordEncoderPasswordEncoder delegate = this.idToPasswordEncoder.get(id);//如果找不到对应的 PasswordEncoder,则使用默认 PasswordEncoder 进行匹配判断if (delegate == null) {return this.defaultPasswordEncoderForMatches.matches(rawPassword, prefixEncodedPassword);}//从存储的密码字符串中抽取密文,去掉 idString encodedPassword = extractEncodedPassword(prefixEncodedPassword);//使用对应 PasswordEncoder 针对密文进行匹配判断return delegate.matches(rawPassword, encodedPassword);
}

Spring Security 加密模块

加解密技术是一种通用的基础设施类技术,不仅可以用于用户认证,也可以用于其他任何涉及敏感数据处理的场景。因此,Spring Security 也充分考虑到了这种需求,专门提供了一个加密模式(Spring Security Crypto Module,SSCM)。

请注意,尽管 PasswordEncoder 也属于这个模块的一部分,但这个模块本身是高度独立的,我们可以脱离于用户认证流程来使用这个模块。

Spring Security 加密模块的核心功能有两部分,首先就是加解密器(Encryptors),典型的使用方式如下:

BytesEncryptor e = Encryptors.standard(password, salt);

上述方法使用了标准的 256 位 AES 算法对输入的 password 字段进行加密,返回的是一个 BytesEncryptor。同时,我们也看到这里需要输入一个代表盐值的 salt 字段,而这个 salt 值的获取就可以用到 Spring Security 加密模块的另一个功能——键生成器(Key Generators),使用方式如下所示:

String salt = KeyGenerators.string().generateKey();

上述键生成器会创建一个 8 字节的密钥,并将其编码为十六进制字符串。

如果将加解密器和键生成器结合起来,我们就可以实现通用的加解密机制,如下所示:

String password = "kdfljxcasd";String encodeThat = "Hello World + some special chars!^^-";String salt = KeyGenerators.string().generateKey();BytesEncryptor encryptor = Encryptors.standard(password, salt);// breakpoint steping doesn't reach that point - it gets stuck here for some reason... byte[] encrypted = encryptor.encrypt(encodeThat.getBytes());byte[] decrypted = encryptor.decrypt(encrypted);System.out.println("Before encryption: " + encodeThat);     System.out.println("Encrypted: " + encrypted.toString());       System.out.println("After encryption: " + decrypted.toString());
http://www.lbrq.cn/news/1102591.html

相关文章:

  • 六安网站关键词排名优化报价/深圳优化公司样高粱seo
  • 上海哪家公司做网站好/网络营销的市场背景
  • 怎么做考试资料分享网站/seo快速排名是什么
  • 怎样在我的世界做汽车视频网站/免费建网站软件哪个好
  • 网站的竞价怎么做/青岛seo优化公司
  • 网上写作真正能赚钱的网站/友链互换平台推荐
  • 招聘网站上找在家做/百度付费推广
  • 政府门户网站建设取得/快速seo优化
  • 网站建设深/网店运营实训报告
  • 2023免费推广网站/百度下载安装到桌面上
  • 网站建设支付/百度sem认证
  • 深圳电商网站开发公司/镇江百度推广
  • 果洛wap网站建设公司/搜索引擎排名机制
  • 大理微网站建设/网络营销公司哪家好
  • 电商客服外包加盟代理/网站优化公司开始上班了
  • 做网站还要做点手机吗/数据分析师要学什么
  • 网站建设技术参数/企业网站优化价格
  • 做桑拿网站犯法吗/福州seo推广外包
  • 四川省建筑施工企业特种作业人员/长沙网站优化推广
  • 重庆巨能建设集团网站/广州疫情防控措施
  • 中装建设官方网站/自媒体营销模式有哪些
  • 移动门网站建设/网络推广网络营销外包
  • 做彩票网站代理/哪里有竞价推广托管
  • 玉溪网站建设网站建设/网页生成
  • wordpress 发布时间不对/沧浪seo网站优化软件
  • wordpress 图片默认居中/杭州seo托管公司推荐
  • 国外优秀企业网站模板/seo基础培训机构
  • 洛阳响应式网站建设/百度官方app下载
  • 网站建设 技术/百度首页推广
  • 内地公司 香港服务器 经营性网站/怎么找百度客服
  • 调试Claude code的正确姿势
  • Python 单例模式几种实现方式
  • 【RK3576】【Android14】ADB工具说明与使用
  • 神经网络:池化层
  • 【愚公系列】《MIoT.VC》002-构建基本仿真工作站(布局一个基本工作站)
  • 【Java源码阅读系列56】深度解读Java Constructor 类源码