Spring Security-全面详解(学习总结---从入门到深化)(上)
Spring Security介绍
Spring Security是Spring项目组提供的安全服务框架,核心功能包 括认证和授权。它为系统提供了声明式安全访问控制功能,减少了 为系统安全而编写大量重复代码的工作。
认证
认证即系统判断用户的身份是否合法,合法可继续访问,不合法则 拒绝访问。常见的用户身份认证方式有:用户名密码登录、二维码 登录、手机短信登录、脸部识别认证、指纹认证等方式。 认证是为了保护系统的隐私数据与资源,用户的身份合法才能访问 该系统的资源。
授权
授权即认证通过后,根据用户的权限来控制用户访问资源的过程, 拥有资源的访问权限则正常访问,没有权限则拒绝访问。 比如在一 些视频网站中,普通用户登录后只有观看免费视频的权限,而VIP用 户登录后,网站会给该用户提供观看VIP视频的权限。 认证是为了保证用户身份的合法性,授权则是为了更细粒度的对隐 私数据进行划分,控制不同的用户能够访问不同的资源。
举个例子:认证是公司大门识别你作为员工能进入公司,而授权则 是由于你作为公司会计可以进入财务室,查看账目,处理财务数据。网络异常,图片无法展示|
Spring Security认证_项目搭建
接下来我们先来搭建一个Spring Security项目
1、准备一个名为 mysecurity 的Mysql数据库
2、创建SpringBoot项目,添加依赖
<!-- SpringMVC --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!--Thymeleaf--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <!--Spring Security--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <!-- Mysql驱动 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <!-- MyBatisPlus --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.0</version> </dependency> <!-- lombok --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <!-- junit --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency>
3、为SpringBoot项目编写配置文件
server: port: 80 #日志格式 logging: pattern: console: '%d{HH:mm:ss.SSS} %clr(%-5level) --- [%-15thread] %cyan(%-50logger{50}):%msg%n' # 数据源 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql:///mysecurity?serverTimezone=UTC username: root password01: root
4、在 template 文件夹编写项目主页面 main.html
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>主页面</title> </head> <body> <h1>主页面</h1> </body> </html>
5、编写访问页面控制器
@Controller public class PageController { @RequestMapping("/{page}") public String showPage(@PathVariable String page){ return page; } }
启动项目,访问项目主页面http://localhost/main,项目会自动 跳转到一个登录页面。这代表Spring Security已经开启了认证 功能,不登录无法访问所有资源,该页面就是Spring Security 自带的登录页面。 我们使用 user 作为用户名,控制台中的字符串作为密码登录,登 录成功后跳转到项目主页面。 在后续的课程中,我们会讲解在真实开发中,如何对登录页 面、登录逻辑等进行自定义配置。
Spring Security认证_内存认证
在实际开发中,用户数量不会只有一个,且密码是自己设置的。所 以我们需要自定义配置用户信息。首先我们在内存中创建两个用 户,Spring Security会将登录页传来的用户名密码和内存中用户名 密码做匹配认证。
// Security配置类 @Configuration public class SecurityConfig { // 定义认证逻辑 @Bean public UserDetailsService userDetailsService(){ // 1.使用内存数据进行认证 InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager(); // 2.创建两个用户 UserDetails user1 = User.withUsername("baizhan").password("123").authorities("admin").build(); UserDetails user2 = User.withUsername("sxt").password("456").authorities("admin").build(); // 3.将这两个用户添加到内存中 manager.createUser(user1); manager.createUser(user2); return manager; } //密码编码器,不解析密码 @Bean public PasswordEncoder passwordEncoder() { return NoOpPasswordEncoder.getInstance(); } }
此时进行认证测试,我们可以将登录页传来的用户名密码和内存中 用户名密码做匹配认证。
Spring Security认证_UserDetailsService
在实际项目中,认证逻辑是需要自定义控制的。将 UserDetailsService 接口 的实现类放入Spring容器即可自定义认证逻辑。 InMemoryUserDetailsManager 就是 UserDetailsService 接口的一个实现类,它将登 录页传来的用户名密码和内存中用户名密码做匹配认证。当然我们 也可以自定义 UserDetailsService 接口的实现类。
public interface UserDetailsService { UserDetails loadUserByUsername(String username) throws UsernameNotFoundException; }
UserDetailsService 的实现类必须重写 loadUserByUsername 方法,该方法定义了 具体的认证逻辑,参数 username 是前端传来的用户名,我们需要根据 传来的用户名查询到该用户(一般是从数据库查询),并将查询到 的用户封装成一个UserDetails对象,该对象是Spring Security提供 的用户对象,包含用户名、密码、权限。Spring Security会根据 UserDetails对象中的密码和客户端提供密码进行比较。相同则认证 通过,不相同则认证失败。
Spring Security认证_数据库认证
接下来我们连接数据库进行认证:
1、准备数据库数据
CREATE TABLE `users` ( `id` int(11) NOT NULL AUTO_INCREMENT, `username` varchar(255), `password` varchar(255) , `phone` varchar(255) , PRIMARY KEY (`id`) ); INSERT INTO `users` VALUES (1, 'bazhn','bazhn', '13812345678'); INSERT INTO `users` VALUES (2, 'xt','xt', '13812345678');
2、编写用户实体类
@Data public class Users { private Integer id; private String username; private String password; private String phone; }
3、编写dao接口
public interface UsersMapper extends BaseMapper<Users> {}
4、在SpringBoot启动类中添加 @MapperScan 注解,扫描Mapper文件夹
@SpringBootApplication @MapperScan("com.itbaizhan.myspringsecurity.mapper") public class MysecurityApplication { public static void main(String[] args) { SpringApplication.run(MysecurityApplication.class, args); } }
5、创建 UserDetailsService 的实现类,编写自定义认证逻辑
@Service public class MyUserDetailsService implements UserDetailsService { @Autowired private UsersMapper usersMapper; // 自定义认证逻辑 @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { // 1.构造查询条件 QueryWrapper<Users> wrapper = new QueryWrapper<Users>().eq("username",username); // 2.查询用户 Users users = usersMapper.selectOne(wrapper); // 3.封装为UserDetails对象 UserDetails userDetails = User .withUsername(users.getUsername()) .password(users.getPassword()) .authorities("admin") .build(); // 4.返回封装好的UserDetails对象 return userDetails; } }
6、测试连接数据库认证
Spring Security认证_PasswordEncoder
在实际开发中,为了数据安全性,在数据库中存放密码时不会存放 原密码,而是会存放加密后的密码。而用户传入的参数是明文密 码。此时必须使用密码解析器才能将加密密码与明文密码做比对。 Spring Security中的密码解析器是 PasswordEncoder 。
Spring Security要求容器中必须有 PasswordEncoder 实例,之前使用的
NoOpPasswordEncoder 是 PasswordEncoder 的实现类,意思是不解析密码,使用 明文密码。
Spring Security官方推荐的密码解析器是 BCryptPasswordEncoder 。接下来 我们学习 BCryptPasswordEncoder 的使用。
@SpringBootTest public class PasswordEncoderTest { @Test public void testBCryptPasswordEncoder(){ //创建解析器 PasswordEncoder encoder = new BCryptPasswordEncoder(); //密码加密 String password = encoder.encode("baizhan"); System.out.println("加密后:"+password); //密码校验 /** * 参数1:明文密码 * 参数2:加密密码 * 返回值:是否校验成功 */ boolean result = encoder.matches("baizhan","$2a$10$/MImcrpDO21HAP2amayhme8j2SM0YM50/WO8YBH.NC1hEGGSU9ByO"); System.out.println(result); } }