Spring Security 基础教程 -- 密码加密和方法安全

密码加密

一般来说,密码是需要加密再存入数据库的,常用哈希函数进行加密。

密码加密与通信加密是有区别的。

通信加密是可逆加密,加密之后还需要解密,主要有对称加密和非对称加密两种。密码加密可以是单向加密,即加密之后不需要解密。

为了保证相同的明文加密后生成的密文不一样,在加密过程中需要使用盐(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

这就是方法安全。

每天学习一点点,每天进步一点点。

原文地址:https://www.cnblogs.com/youcoding/p/13915385.html