1 介绍
任何应用考虑到安全,绝不能明文的方式保存密码。密码应该通过哈希算法进行加密。有很多标准的算法比 SHA 或者 MD5,结合 salt (盐)是一个不错的选择。 Spring Security 提供了 BCryptPasswordEncoder 类,实现 Spring 的 PasswordEncoder 接口使用 BCrypt 强哈希方法来加密密码。
BCrypt 强哈希方法每次加密的结果都不一样。
2 准备工作
2.1 引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
2.2 添加配置类
/**
* 安全配置类
*/
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/**").permitAll()
.anyRequest().authenticated()
.and().csrf().disable();
}
}
说明:
- @Configuration:用于定义配置类的注解。
- @EnableWebSecurity:是 Spring Security 用于启用 Web 安全的注解。
- authorizeRequests():所有 security 全注解配置实现的开端,表示开始说明需要的权限。需要的权限分为两个部分:1.拦截的路径;2.访问该路径需要的权限。
- antMatchers():表示拦截什么路径。
- permitAll():表示任何权限到可以访问,直接放行所有。
- anyRequest():任何的请求。
- authenticated():认证之后才能访问。
- .and().csrf().disable():固定写法,表示使 csrf 拦截失效。
2.3 把 BCryptPasswordEncoder 类注入容器
@SpringBootApplication
public class UserApplication {
public static void main(String[] args) {
SpringApplication.run(UserApplication.class, args);
}
@Bean
public IdWorker idWorker() {
return new IdWorker(1, 1);
}
@Bean
public BCryptPasswordEncoder bcryptPasswordEncoder(){
return new BCryptPasswordEncoder();
}
}
3 加密
3.1 Service 层
这里我在 service 层调用 BCryptPasswordEncoder
的 public String encode(CharSequence rawPassword)
方法对前端传过来的密码进行加密。
@Service
public class AdminService {
@Autowired
AdminDao adminDao;
@Autowired
IdWorker idWorker;
@Autowired
BCryptPasswordEncoder bCryptPasswordEncoder;
public void addAdmin(Admin admin) {
admin.setId(idWorker.nextId() + "");
admin.setPassword(bCryptPasswordEncoder.encode(admin.getPassword()));
adminDao.save(admin);
}
}
3.2 dao 层
dao 层用的 Spring JPA 和数据库交互。
public interface AdminDao extends JpaRepository<Admin, String>, JpaSpecificationExecutor<Admin> {
}
3.3 controller 层
@RequestMapping(value = "/admin")
@RestController
@CrossOrigin
public class AdminController {
@Autowired
AdminService adminService;
@PostMapping
public Result addAdmin(@RequestBody Admin admin) {
adminService.addAdmin(admin);
return new Result(StatusCode.OK, true, "增加管理员成功");
}
}
3.4 测试
前端的密码是 123,经过 BCrypt 加密之后,数据库中保存的内容就是一串乱码了,加密成功。
4 密码校验
4.1 AdminDao 增加方法定义
用过登录名从数据库中获取管理员信息。
public interface AdminDao extends JpaRepository<Admin, String>, JpaSpecificationExecutor<Admin> {
Admin findByLoginname(String loginname);
}
4.2 AdminService 增加方法
Service 层添加一个 Admin login(Admin admin)
方法用于登录。
注意:public boolean matches(CharSequence rawPassword, String encodedPassword)
方法中,第一位填写 “生密码” 即手动输入的密码,第二位填写 “加密之后的密码” 即数据库中保存的密码。
public Admin login(Admin admin) {
Admin adminFromSQL = adminDao.findByLoginname(admin.getLoginname());
if (adminFromSQL != null && bCryptPasswordEncoder.matches(admin.getPassword(), adminFromSQL.getPassword())) {
return adminFromSQL;
}
return null;
}
4.3 AdminController 增加方法
@PostMapping(value = "/login")
public Result login(@RequestBody Admin admin) {
Admin adminLogin = adminService.login(admin);
if (adminLogin == null) {
return new Result(StatusCode.ERROR, false, "登录失败");
}
return new Result(StatusCode.OK, true, "登录成功");
}
4.4 测试
我们的密码是 123,输入 111 时,返回登录失败。