密码加密
一般来说,密码是需要加密再存入数据库的,常用哈希函数进行加密。
密码加密与通信加密是有区别的。
通信加密是可逆加密,加密之后还需要解密,主要有对称加密和非对称加密两种。密码加密可以是单向加密,即加密之后不需要解密。
为了保证相同的明文加密后生成的密文不一样,在加密过程中需要使用盐(salt)。
在 Spring Security 中,提供了BCryptPasswordEncoder
类,进行密码的加密,且相同的明文,加密后的密文是不一样的。
示例
在测试类中,写一个测试方法:
@SpringBootTest
class SecurityApplicationTests {
@Test
void contextLoads() {
for (int i=0; i<10; i++){
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
System.out.println(encoder.encode("123456"));
}
}
}
打印结果如下:
$2a$10$/zpce9GF4fW0xmfCWZk2MuHSX9frcj1JIMk7n1TPcUHDfU1clqy/i
$2a$10$Ihh9bkrN4KQjRF4BNbCwdeUgu779riYXofpVZu9djIjix2wAJo8tC
$2a$10$BnMc9GSCLKNrgXpKRgHNYucQqcm/Mu.W21pfmm/DEKvQ0qoeAqYs6
$2a$10$t2HljppbnA7NbBllkqgNB.lE655sD2ZPW6F2Y9ITZiEMJJTZUxTpS
$2a$10$WyZkBbv1VakuTJFm9AbEHOgtIt1ZjIPEIDpOLNdOnEjvcW.t34Vki
$2a$10$Lq3/QQLIgqiicGfEZL1YP.OVZ46X2WfPMOAz6iMJnNm/oWUs4/F1.
$2a$10$2e1PSKN80/WSWsxdUcrSHe2EmD30M4IZfQog6mGw9H5Ul1jfu63aK
$2a$10$VZcLRImtB18O1aV29ShRrONzygpCigBO3eROSyJ2035.AVkrgKpPa
$2a$10$SW9ydYyb9BCnmSPSmdg2P.NP4XBduCDIlnl.hxIh3/M/d/fUr83DK
$2a$10$hLWCemE7MVWtczl57shsQeF9nUILLmG0tooKh61sKW3Un17o18JSC
可以看出,相同的明文 123456 ,生成的 10 次密文都是不一样的。
在方法configure(AuthenticationManagerBuilder auth)
,将明文密码替换成密文密码:
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
/**
* 调用加密算法 BCryptPasswordEncoder
*/
@Bean
PasswordEncoder passwordEncoder(){
// return NoOpPasswordEncoder.getInstance();
return new BCryptPasswordEncoder();
}
/**
* 定义两个用户,并设置密码和角色
* 从 Spring5.0 开始,密码必须要加密
* 基于内存的用户认证
* @param auth
* @throws Exception
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("admin")
.password("$2a$10$9UwAaMnKSZHUloUl65mpWu01VF5ANLrmD/rsb6eGP4bc2f45.1.iG")
.roles("admin")
.and()
.withUser("user1")
.password("$2a$10$JnwOr9kXPg97mgOkUG2Qhe7b0Qrtr9BDm7G410p8sr6SPJoZupxP2")
.roles("user");
}
}
关于 BCryptPasswordEncoder
的加密、验证策略的源码分析,参考文章:BCryptPasswordEncoder加密、验证策略
方法安全
方法安全,即是直接在方法上加注解,来确保方法安全地执行。
示例
方法安全默认是关闭的,使用方法安全,首先需要在 SecurityConfig
配置类上,加注解:
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
新建一个 MethodService 类,在方法上进行安全注解:
@Service
public class MethodService {
/**
* 必须是角色 admin 才能调用这个方法
*/
@PreAuthorize("hasRole('admin')")
public String admin(){
return "hello admin!";
}
/**
* 只有角色 user 才能调用这个方法
* @return
*/
@Secured("ROLE_user")
public String user(){
return "hello user!";
}
/**
* 角色 admin和user 都可以调用这个方法
* @return
*/
@PreAuthorize("hasAnyRole('admin', 'user')")
public String hello(){
return "hello hello!";
}
}
再注入到 Controller 类中,测试一下 MethodService 中的方法:
@Autowired
MethodService methodService;
@GetMapping("/hello1")
public String hello1(){
return methodService.admin();
}
@GetMapping("/hello2")
public String hello2(){
return methodService.user();
}
@GetMapping("/hello3")
public String hello3(){
return methodService.hello();
}
如果使用角色 admin 登录,就能成功调用接口 /hello1
和/hello3
。
如果使用角色 user 登录,就能成功调用接口 /hello2
和/hello3
这就是方法安全。
每天学习一点点,每天进步一点点。