哈希算法,又称摘要算法(Digest),是一种将任意长度的输入通过散列函数变换成固定长度的输出的单向密码体制。这种映射的规则就是哈希算法,而通过原始数据映射之后得到的二进制值串就是哈希值。
哈希算法最重要的特点就是:相同的输入一定得到相同的输出,不同的输入可能得到相同的输出,但不可能通过输出反推出原始输入。这意味着哈希算法可以用于快速比较两个数据是否相同,常用于密码存储、数字签名、数据完整性校验等领域。
- 唯一性。数据通过hash算法计算的hash值是唯一的
- 压缩性。例如,任意长度的数据,算出的MD5值的长度是固定的(128位二进制数,32位十六进制数)
- 不可逆。无法从结果复原源数据信息
- 抗修改。对原数据的任何改动,hash值完全不同
- 强抗碰撞。伪造数据非常困难
- 容易计算。从原数据计算出值很容易
Hash算法无法转换回源数据,因此是签名算法,不是加密/解密算法(无解密)
即,仅判断是不是源数据,不知道源数据是什么。因此适合,验证敏感源数据的正确性。例如,验证密码(如何判断密码正确?)?
Salt(盐)是在密码学中常用的一种安全措施,其本质是一段随机的字符串。在密码加密过程中,Salt会被添加到原始密码中,再通过散列函数进行散列,最终生成一个唯一的散列值。
Salt的主要作用是增加破解密码的难度,因为即使两个用户使用了相同的密码,由于Salt的存在,他们的密码散列值也会不同。这就意味着,攻击者即使获取了数据库中的密码散列值,也无法通过简单的对比找到匹配的原始密码。
Spring提供了一套安全框架,处理加密/解密数据信息 提供了包括对称/非对称加密,不同Hash算法等一系列实现
?相关配置
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
相关接口
PasswordEncoder接口是Spring Security中用于对密码进行加密的接口。它提供了一种通用的方法来将明文密码转换为加密后的密码,以便在存储和验证过程中使用。
- String encode(CharSequence rawPassword),编码密码
- boolean matches(CharSequence rawPassword, String encodedPassword),验证原始密码与编码密码
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
public class PasswordEncoderExample {
public static void main(String[] args) {
// 创建PasswordEncoder实例
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
// 原始密码
String plainPassword = "myPassword";
// 加密密码
String encodedPassword = passwordEncoder.encode(plainPassword);
System.out.println("原始密码: " + plainPassword);
System.out.println("加密后的密码: " + encodedPassword);
}
}
?Pbkdf2PasswordEncoder类是Spring Security中的一个密码编码器,它使用PBKDF2算法对密码进行加密。PBKDF2是一种密钥导出函数,它可以从用户输入的密码生成一个足够复杂的密钥,以保护存储在数据库中的密码。
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
public class PasswordEncoderExample {
public static void main(String[] args) {
// 创建Pbkdf2PasswordEncoder对象
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
// 原始密码
String plainPassword = "myPassword";
// 使用Pbkdf2算法加密密码
String encodedPassword = passwordEncoder.encode(plainPassword);
System.out.println("原始密码: " + plainPassword);
System.out.println("加密后的密码: " + encodedPassword);
}
}
BCryptPasswordEncoder类是Spring Security中的一个密码编码器,它使用Bcrypt算法对密码进行加密。Bcrypt是一种加密算法,它可以生成一个足够复杂的哈希值来保护存储在数据库中的密码。自动生成随机盐值,并附在结果,避免盐值的单独保存
- 128bits随机二进制数,16bytes,base64,24chars,特殊算法转为22chars
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
public class PasswordEncoderExample {
public static void main(String[] args) {
// 创建BCryptPasswordEncoder对象
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
// 原始密码
String plainPassword = "myPassword";
// 使用Bcrypt算法加密密码
String encodedPassword = passwordEncoder.encode(plainPassword);
System.out.println("原始密码: " + plainPassword);
System.out.println("加密后的密码: " + encodedPassword);
}
}
配置类
package com.passwordencoder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
public class SecurityConfiguration {
// 定义一个名为getPasswordEncoder的方法,返回类型为PasswordEncoder
@Bean
public PasswordEncoder getPasswordEncoder() {
// 创建一个新的BCryptPasswordEncoder对象并返回
return new BCryptPasswordEncoder();
}
}
状态类
package com.passwordencoder.vo;
import lombok.Builder;
import lombok.Data;
import java.util.Map;
@Data
@Builder
public class ResultVO {
private int code;
private String message;
private Map<String, Object> data;
public static ResultVO success(Map<String, Object> data) {
return ResultVO.builder().code(200).data(data).build();
}
public static ResultVO error(int code, String msg) {
return ResultVO.builder().code(code).message(msg).build();
}
}
实体类
package com.passwordencoder.entity;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class User04 {
private String userName;
private String password;
}
服务类
package com.example.springmvcexamples.example04.passwordencoder.service;
import com.example.springmvcexamples.example04.passwordencoder.entity.User04;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@Slf4j // 使用lombok的注解,简化代码并自动生成getter、setter等方法
@Service // 标记这是一个Spring服务类
public class UserService04 {
// 根据用户名获取用户信息的方法
public User04 getUser(String userName) {
// 如果用户名为"BO",则返回一个包含用户名和加密密码的用户对象
return "BO".equals(userName)
? User04.builder() // 使用User04的构建器模式创建一个新的用户对象
.userName("BO") // 设置用户名为"BO"
.password("$2a$10$A7OcKw5xxRMh9c4ghWySr.Rjh22gpWyiWExZO5i2B32eJLQrFXcr6") // 设置加密后的密码
.build() // 构建并返回用户对象
: null; // 如果用户名不为"BO",则返回null
}
}
处理类
package com.passwordencoder.controller;
import com.passwordencoder.entity.User04;
import com.passwordencoder.service.UserService04;
import com.passwordencoder.vo.ResultVO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Map;
@Slf4j
@RestController
@RequestMapping("/api/example04/")
@RequiredArgsConstructor
public class ExampleController04 {
private final UserService04 userService;
private final PasswordEncoder passwordEncoder;
@PostMapping("login")
public ResultVO login(@RequestBody User04 user) {
// 先查询用户是否存在
User04 u = userService.getUser(user.getUserName());
if (u == null || !passwordEncoder.matches(user.getPassword(), u.getPassword())) {
log.debug("登录失败");
return ResultVO.error(401, "用户名密码错误");
}
// 登录成功,添加token等操作
log.debug("登录成功");
return ResultVO.success(Map.of("user", u));
}
}
测试
POST http://localhost:8081/api/example04/login
Content-Type: application/json
{
"userName": "BO",
"password": "12345"
}
- SpringMVC默认基于Jackson实现序列化/反序列化
- SpringMVC自动注入Jackson ObjectMapper映射对象到容器
- String writeValueAsString(T payload),将对象序列化为json字符串
- T readValue(String content, Class c),将json字符串反序列化为指定类型的Java对象
TypeReference<T>抽象类。创建子类,具体化泛型。可通过创建类似接口的匿名内部类实现
Token令牌是一种用于身份验证和授权的凭证,通常由服务器生成并发送给用户。它包含有关用户的信息,例如用户名、角色等,以及一些会话信息,例如过期时间等。当用户尝试访问受保护的资源时,他们需要提供有效的Token令牌以证明其身份和权限。服务器将验证Token令牌的有效性,并根据其中包含的信息授予或拒绝用户的请求。
Restful设计思想,服务器端不再保存用户状态(无HttpSession)
- 用户登录后,将用户身份/权限信息封装在Token(令牌)
- ·将token信息加密(Authorization)通过http header返给客户端
- ·客户端每次需要身份/权限的请求,均需在http header携带Authorization
- ·服务器端拦截权限请求,从Authorization中解密出Token权鉴
实现
- ·JWT。流行的认证标准,信息由header/payload/,signature组成,多种实现
- ·自定义Token。更灵活,数据量小
加密/解密算法,适合敏感数据的加密传输
- 对称加密算法(AES等),通过相同密钥加密/解密
- 非对称加密算法(RSA等),公钥加密的数据,必须通过私钥才能解密?
?
?