SpringBoot 整合 SpringSecurity——入门
SpringBoot 整合 SpringSecurity——入门
0、 环境搭建
新建一个 SpringBoot 项目
-
导入依赖:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId></dependency><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId></dependency><dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional></dependency><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope></dependency><dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-test</artifactId> <scope>test</scope></dependency>
-
在 static 包下新建一个 index.html
<html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <h1>首页</h1> </body></html>
-
启动工程
在控制台得到一串字符串
Using generated security password: fa3865d2-b254-4d23-b43f-91cc55a3b4c6
此为 SpringSecurity 自动生成的登录页面的密码
-
运行效果
1、Spring Security 原理
Spring Security本质是一个过滤器链有很多过滤器
- FilterSecurityInterceptor :是一个方法级的权限过滤器,基本位于过滤链的最底部
- ExceptionTranslationFilter :是个异常过滤器,用来处理在认证授权过程中轮出的异常
- UsernamePesswordAuthenticationFilter : 对/login的POST请求做拦截,校验表单中用户名,密码。
1.1、权限管理中的相关概念
-
主体
英文单词:principal
使用系统的用户或设备或从其他系统远程登录的用户等等。简单说就是谁使用系统谁就是主体。
-
认证
英文单词:authentication
权限管理系统确认一个主体的身份,允许主体进入系统。简单说就是“主体”证明自己是谁。
笼统的认为就是以前所做的登录操作。
-
授权
英文单词:authorization
将操作系统的“权力”“授予”“主体”,这样主体就具备了操作系统中特定功能的能力。
所以简单来说,授权就是给用户分配权限。
2、UserDetails
package org.springframework.security.core.userdetails;import java.io.Serializable;import java.util.Collection;import org.springframework.security.core.Authentication;import org.springframework.security.core.GrantedAuthority;/** * Provides core user information. * * * Implementations are not used directly by Spring Security for security purposes. They * simply store user information which is later encapsulated into {@link Authentication} * objects. This allows non-security related user information (such as email addresses, * telephone numbers etc) to be stored in a convenient location. *
* Concrete implementations must take particular care to ensure the non-null contract * detailed for each method is enforced. See * {@link org.springframework.security.core.userdetails.User} for a reference * implementation (which you might like to extend or use in your code). * * @author Ben Alex * @see UserDetailsService * @see UserCache */
public interface UserDetails extends Serializable {// 表示获取登录用户所有权限Collection<? extends GrantedAuthority> getAuthorities();// 表示获取密码String getPassword();// 表示获取用户名String getUsername();// 表示判断账户是否过期boolean isAccountNonExpired();// 表示判断账户是否被锁定boolean isAccountNonLocked();// 表示凭证{密码}是否过期boolean isCredentialsNonExpired();// 表示当前用户是否可用boolean isEnabled();}
这个类是系统默认的用户“主体”
3、UserDetailsService
UserDetailsService 接口实现类
当什么也没有配置的时候,账号和密码是由 Spring Security定义生成的。而在实际项目中账号和密码都是从数据库中查询出来的。所以我们要通过自定义逻辑控制认证逻辑。如果需要自定义逻辑时,只需要实现UserDetailService 接口即可。UserDetailService 接口定义如下:
package org.springframework.security.core.userdetails;/** * Core interface which loads user-specific data. * * It is used throughout the framework as a user DAO and is the strategy used by the * {@link org.springframework.security.authentication.dao.DaoAuthenticationProvider * DaoAuthenticationProvider}. * *
* The interface requires only one read-only method, which simplifies support for new * data-access strategies. * * @author Ben Alex * @see org.springframework.security.authentication.dao.DaoAuthenticationProvider * @see UserDetails */
public interface UserDetailsService {/** * Locates the user based on the username. In the actual implementation, the search * may possibly be case sensitive, or case insensitive depending on how the * implementation instance is configured. In this case, the UserDetails
* object that comes back may have a username that is of a different case than what * was actually requested.. * @param username the username identifying the user whose data is required. * @return a fully populated user record (never null
) * @throws UsernameNotFoundException if the user could not be found or the user has no * GrantedAuthority */UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;}
loadUserByUsername
:根据用户名定位用户。在实际实现中,搜索可能区分大小写,也可能不区分大小写,具体取决于实现实例的配置方式。在这种情况下,返回的 UserDetails 对象的用户名可能与实际请求的用户名不同
方法参数 username
(表示用户名。此值是客户端表单传递过来的数据。默认情况下必须叫 username,否则无
法接收)
返回值 UserDetails
4、PasswordEncoder 密码解析器
SpringSecurity 要求容器中必须有PasswordEncoder实例。所以当自定义登录逻辑时要求必须给容器注入PaswordEncoder的bean对象。
4.1、接口介绍
- encode():把参数按照特定的解析规则进行解析。
- matches():验证从存储中获取的编码密码与编码后提交的原始密码是否匹配。如果密码匹配,则返回true;如果不匹配,则返回false。第一个参数表示需要被解析的密码。第二个参数表示存储的密码。
- upgradeEncoding():如果解析的密码能够再次进行解析且达到更安全的结果则返回true,否则返回false。默认返回false.
4.2、PaswordEncoder 实现类
在 PaswordEncoder 中内置了很多的实现类
4.3、BCryptPasswordEncoder
BCryptPasswordEncoder 是 Spring Security官方推荐的密码解析器,平时多使用这个解析器。
BCryptPasswordEncoder是对 berypt 强散列方法的具体实现。是基于Hash算法实现的单向加密。可以通过strength控制加密强度,默认10.
4.4、代码演示
新建测试 BCryptPasswordEncoder 用法
-
加密密码:
@Testpublic void testPasswordEncoder(){ PasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); String beEncodePassword = passwordEncoder.encode("WuYufan"); System.out.println("beEncodePassword = " + beEncodePassword);}
运行结果:
beEncodePassword = $2a$10$Nm26wey7CCxhbzuXfpgO4.UmVpJxfiekX2B2YNNfbn7hmJo3OKc4K
-
密码比对
@Testpublic void testPasswordMatches(){ PasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); boolean isMatches = passwordEncoder.matches("WuYufan","$2a$10$Nm26wey7CCxhbzuXfpgO4.UmVpJxfiekX2B2YNNfbn7hmJo3OKc4K"); System.out.println("isMatches = " + isMatches);}
运行结果:
isMatches = true
5、设置用户名和密码
5.1、方式一:通过配置文件
在 application.yml 中增加配置
spring: security: user: name: wuyufan password: 123465
5.2、方式二:通过配置类实现
package cn.edu.hziee.config;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;import org.springframework.security.crypto.password.PasswordEncoder;@Configurationpublic class SpringSecurityConfig extends WebSecurityConfigurerAdapter { @Bean public PasswordEncoder passwordEncoder(){ return new BCryptPasswordEncoder(); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { String password = passwordEncoder().encode("123456"); auth.inMemoryAuthentication().withUser("wuyufan").password(password).roles("admin"); }}
5.3、方式三:自定义实现类设置(推荐)
-
创建配置类,设置使用哪个 UserDetailsService 实现类
package cn.edu.hziee.config;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;import org.springframework.security.crypto.password.PasswordEncoder;@Configurationpublic class SpringSecurityConfig extends WebSecurityConfigurerAdapter { @Bean public PasswordEncoder passwordEncoder(){ return new BCryptPasswordEncoder(); }}
-
编写实现类,返回 User 对象,User 对象有用户名、密码和操作权限
package cn.edu.hziee.service.impl;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.security.core.GrantedAuthority;import org.springframework.security.core.authority.AuthorityUtils;import org.springframework.security.core.userdetails.User;import org.springframework.security.core.userdetails.UserDetails;import org.springframework.security.core.userdetails.UserDetailsService;import org.springframework.security.core.userdetails.UsernameNotFoundException;import org.springframework.security.crypto.password.PasswordEncoder;import org.springframework.stereotype.Service;import java.util.List;@Service("userDetailsService")public class UserServiceImpl implements UserDetailsService { @Autowired private PasswordEncoder passwordEncoder; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { if(!("wuyufan".equals(username))){ System.out.println("用户名不存在!"); return null; } String password = passwordEncoder.encode("123456"); List<GrantedAuthority> authorities = AuthorityUtils.commaSeparatedStringToAuthorityList("admin,normal"); return new User(username, password, authorities); }}
上一篇:Spring Security 概述
下一篇:SpringBoot 整合 SpringSecurity——数据库实现