Spring Security是用于解决认证与授权的框架。
在根项目下创建新的xxx子模块,最基础的依赖项包括spring-boot-starter-web与spring-boot-starter-security(为避免默认存在的测试类出错,应该保留测试的依赖项spring-boot-starter-test),完整的xxx的pom.xml为:
- <?xml version="1.0" encoding="UTF-8"?>
- <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
- <modelVersion>4.0.0</modelVersion>
-
- <!-- 父级项目 -->
- <parent>
- <groupId>xxx</groupId>
- <artifactId>xxx</artifactId>
- <version>xxx</version>
- </parent>
-
- <!-- 当前项目的信息 -->
- <groupId>xxx</groupId>
- <artifactId>xxx</artifactId>
- <version>xxx</version>
-
- <!-- 当前项目需要使用的依赖项 -->
- <dependencies>
- <!-- Spring Boot Web:支持Spring MVC -->
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-web</artifactId>
- </dependency>
- <!-- Spring Boot Security:处理认证与授权 -->
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-security</artifactId>
- </dependency>
- <!-- Spring Boot Test:测试 -->
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-test</artifactId>
- <scope>test</scope>
- </dependency>
- </dependencies>
-
- </project>
调整完成后,即可启动项目,在启动的日志中,可以看到类似以下内容:
Using generated security password: 2abb9119-b5bb-4de9-8584-9f893e4a5a92
Spring Security有默认登录的账号和密码(以上提示的值),密码是随机的,每次启动项目都会不同。
Spring Security默认要求所有的请求都是必须先登录才允许的访问,可以使用默认的用户名user和自动生成的随机密码来登录。在测试登录时,在浏览器访问当前主机的任意网址都可以(包括不存在的资源),会自动跳转到登录页(是由Spring Security提供的,默认的URL是:http://localhost:8080/login),当登录成功后,会自动跳转到此前访问的URL(跳转登录页之前的URL),另外,还可以通过 http://localhost:8080/logout 退出登录。
Spring Security的依赖项中包括了Bcrypt算法的工具类,Bcrypt是一款非常优秀的密码加密工具,适用于对需要存储下来的密码进行加密处理。
-
-
- import org.junit.jupiter.api.Test;
- import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
-
- public class BcryptPasswordEncoderTests {
-
- private BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
-
- @Test
- public void testEncode() {
- // 原文相同的情况,每次加密得到的密文都不同
- for (int i = 0; i < 10; i++) {
- String rawPassword = "123456";
- String encodedPassword = passwordEncoder.encode(rawPassword);
- System.out.println("rawPassword = " + rawPassword);
- System.out.println("encodedPassword = " + encodedPassword);
- }
- // rawPassword = 123456
- // encodedPassword = $2a$10$HWuJ9WgPazrwg9.isaae4u7XdP7ohH7LetDwdlTWuPC4ZAvG.Uc7W
- // encodedPassword = $2a$10$rOwgZMpDvZ3Kn7CxHWiEbeC6bQMGtfX.VYc9DCzx9BxkWymX6FbrS
- // encodedPassword = $2a$10$H8ehVGsZx89lSVHwBVI37OkxWm8LXei4T1o5of82Hwc1rD0Yauhky
- // encodedPassword = $2a$10$meBbCiHZBcYn7zMrZ4fPd.hizrsiZhAu8tmDk.P8QJcCzSQGhXSvq
- // encodedPassword = $2a$10$bIRyvV29aoeJLo6hh1M.yOvKoOud5kC7AXDMSUW4tF/DlcG0bLj9C
- // encodedPassword = $2a$10$eq5BuoAiQ6Uo0.TOPZOFPuRNlPl3t2GoTlaFoYfBu3/Bo3tLzx.v2
- // encodedPassword = $2a$10$DhTSwQfNdqrGgHRmILmNLeV0jt3ZXL435xz0fwyZ315ciI5AuI5gi
- // encodedPassword = $2a$10$T.8/ISoLOdreEEkp4py36O0ZYfihDbdHDuIElZVF3uEgMOX.8sPcK
- // encodedPassword = $2a$10$hI4wweFOGJ7FMduSmcjNBexbKFOjYMWl8hkug0n0k1LNR5vEyhhMW
- // encodedPassword = $2a$10$b4ztMI6tWoiJuoDYKwr7DOywsPkkCdvDxbPfmEsLdp11NdABS7wyy
- }
-
- @Test
- public void testMatches() {
- String rawPassword = "123456";
- String encodedPassword = "$2a$10$hI4wweFOGJ7FMduSmCjNBexbKFOjYMWl8hkug0n0k1LNR5vEyhhMW";
- boolean matchResult = passwordEncoder.matches(rawPassword, encodedPassword);
- System.out.println("match result : " + matchResult);
- }
-
- }
Spring Security的认证机制中包含:当客户端提交登录后,会自动调用UserDetailsService接口(Spring Security定义的)的实现类对象中的UserDetails loadUserByUsername(String username)方法(根据用户名加载用户数据),将得到UserDetails类型的对象,此对象中应该至少包括此用户名对应的密码、权限等信息,接下来,Spring Security会自动完成密码的对比,并确定此次客户端提交的信息是否允许登录!类似于:
- // Spring Security的行为
- UserDetails userDetails = userDetailsService.loadUserByUsername("xxx");
- // Spring Security将从userDetails中获取密码,用于验证客户端提交的密码,判断是否匹配
所以,要实现Spring Security通过数据库的数据来验证用户名与密码(而不是采用默认的user用户名和随机的密码),则在此模块的根包下创建security.UserDetailsServiceImpl类,实现UserDetailsService接口,并重写接口中的抽象方法: