密碼加密
一般來說,密碼是需要加密再存入數據庫的,常用哈希函數進行加密。
密碼加密與通信加密是有區別的。
通信加密是可逆加密,加密之后還需要解密,主要有對稱加密和非對稱加密兩種。密碼加密可以是單向加密,即加密之后不需要解密。
為了保證相同的明文加密后生成的密文不一樣,在加密過程中需要使用鹽(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
這就是方法安全。
每天學習一點點,每天進步一點點。