迎着风跑  

在 Sprig Boot 简单整合 Spring Security

#1. Hello World

创建一个 Spring Boot 应用,并引入依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>
Copied!

编写一个控制器:

@RestController
public class TestController {

    @RequestMapping("/hello")
    public String hello() {
        return "Hello SpringSecurity!";
    }
}
Copied!

然后直接启动项目,访问 http://localhost:8080/login (opens new window)

spring-boot-security-helloworld-01

结果打开的是一个登录页面,其实这时候我们的请求已经被保护起来了,要想访问,需要先登录。

在这个案例中仅仅是引入了一个 Spring Security 的 starter 启动器,没有做任何的配置,而项目已经具有了权限认证。

spring-boot-security-helloworld-02

Spring Security 默认提供了一个用户名为 user 的用户,其密码在控制台可以找到:

spring-boot-security-helloworld-03

成功登录以后就可以正常访问了:

spring-boot-security-helloworld-04

如果想要想修改配置,则应使用 spring.security.user.name 和 spring.security.user.password 。

在 Spring Boot 的配置文件中进行如下配置:

spring.security.user.name=tom
spring.security.user.password=123
Copied!

此时启动项目,将只能通过自己配置的用户名和密码登录。


当然还可以通过配置类的方式进行配置,创建一个配置类去继承 WebSecurityConfigurerAdapter:

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        // 对密码进行加密。123 是密码明文,现在 Spring Security 强制性要求『不允许明文存储密码』。
        BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
        String password = passwordEncoder.encode("123");
        auth.inMemoryAuthentication().withUser("tom").password(password).roles("admin");
    }
}
Copied!

警告

从 5.x 开始,强制性要求必须使用密码加密器(PasswordEncoder)对原始密码(注册密码)进行加密。

因此,如果忘记指定 PasswordEncoder 会导致执行时会出现 There is no PasswordEncoder mapped for the id "null" 异常。

重新启动项目测试一下。会发现登录不上,观察控制台:

spring-boot-security-helloworld-07

这是因为我们在对密码加密的时候使用到了 BCryptPasswordEncoder 对象,而 Spring Security 在对密码比对的过程中不会『自己创建』加密器,因此,需要我们在 Spring IoC 容器中配置、创建好加密器的单例对象,以供它直接使用。

所以,我们还需要在容器中配置、创建加密器的单例对象(上面那个 new 理论上可以改造成注入)

@Bean
public PasswordEncoder passwordEncoder() {
    return new BCryptPasswordEncoder();
}
Copied!

再次重新启动一切正常。

#2. Password Encoder

之前有提及,Spring Security 升级到 5 版本后提高了安全要求:Spring Security 要求所有的密码的存储都『必须』是加密形式的。为此,我们必须要确保 Spring IoC 容器中有一个了 PasswordEncoder 的单例对象用以供 Spring Security 使用。

PasswordEncoder 在两处场景会被使用到:

  • 当我们实现注册功能时,要将用户在前端页面输入的明文密码使用 PasswordEncoder 进行加密后存储、持久化。

  • 当用户在登录时,Spring Security 会将用户在前端页面输入的明文密码使用 PasswordEncoder 进行加密之后,再和由我们提供的密码『标准答案』进行比对。

Spring Security 使用 PasswordEncoder 对你提供的密码进行加密。该接口中有两个方法:加密方法,是否匹配方法。

  • 加密方法(encode)方法在用户注册时使用。在注册功能处,我们(程序员)需要将用户提供的密码加密后存储(至数据库)

  • 匹配方法 matches 方法是由 Spring Security 调用的。在登录功能处,Spring Security 要用它来比较登录密码和密码『标准答案』。

因此上面的配置改为如下形式更为合理:

@Slf4j
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        String password = passwordEncoder().encode("123");
        log.info(password);

        auth.inMemoryAuthentication().withUser("tom")
                .password(password)
                .roles("admin");
    }
}
Copied!

Spring Security 内置的 Password Encoder 有:

加密算法名称PasswordEncoder
NOOP NoOpPasswordEncoder.getInstance()
SHA256 new StandardPasswordEncoder()
BCRYPT(官方推荐) new BCryptPasswordEncoder()
LDAP new LdapShaPasswordEncoder()
PBKDF2 new Pbkdf2PasswordEncoder()
SCRYPT new SCryptPasswordEncoder()
MD4 new Md4PasswordEncoder()
MD5 new MessageDigestPasswordEncoder("MD5")
SHA_1 new MessageDigestPasswordEncoder("SHA-1")
SHA_256 new MessageDigestPasswordEncoder("SHA-256")

上述 Password Encoder 中有一个『无意义』的加密器:NoOpPasswordEncoder 。它对原始密码没有做任何处理(现在也被标记为废弃)

补充

记得使用 @SuppressWarnings("deprecation") 去掉 IDE 的警告信息。

#3. “消失”的登录功能

不知道大家有没有注意到,其实,我们的 Controller 中还没有写登录功能的相关代码(这和 Shiro 非常不同)。但是,之前的示例中,就已经有了完整的『登录』(甚至『退出』)功能,并且,Spring Security 似乎还能记住我们已经登陆过(当我们第二次访问页面时,它不会要求我们再次登录)

posted on 2021-12-02 10:20  迎着风跑  阅读(74)  评论(0编辑  收藏  举报