SpringSecurity学习笔记
SpringSecurity底层本质是一个过滤器链
FilterSecurityInterceptor:是一个方法级的过滤器,位于过滤器链的最底部
ExceptionTranslationFilter:异常过滤器,用来处理认证授权过程中抛出的异常
UsernamePasswordAuthenticationFilter:对/login的POST请求做拦截,校验表单中的用户名和密码。
1、两个重要接口
1、UserDetailsService:从数据库查询用户名和密码的过程
-
创建类继承UsernamePasswordAuthenticationFilter,重写它的三个方法
-
创建类实现UserDetailsService,编写查询数据库的过程,返回一个User对象,这个User对象是SpringSecurity提供的一个对象
2、PasswordEncoder:数据加密的接口,一般用于返回User对象密码的加密
2、认证
-
第一种:application.yml配置文件中配置
spring
-
第二种:通过配置类配置
-
第三种:自定义编写实现类
-
编写UserDetailsService的实现类
package com.zq.security.service;
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.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import java.util.List;
-
编写配置类使用我们自定义的UserDetailsService
package com.zq.security.config;
import org.springframework.beans.factory.annotation.Autowired;
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.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
-
2、通过数据库完成用户认证
1、mybatis-plus依赖
<!--Mybatis-Plus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
</dependency>
2、实体类Users.java
import lombok.Data;
import java.io.Serializable;
3、数据库接口UsersMapper.java
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.zq.security.pojo.Users;
public interface UsersMapper extends BaseMapper<Users> {
}
4、自定义UserDetailsService
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.zq.security.mapper.UsersMapper;
import com.zq.security.pojo.Users;
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;
5、config配置类
import org.springframework.beans.factory.annotation.Autowired;
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.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
3、自定义登录页面和白名单
1、config配置类
3、授权
1、hasAuthority方法:当前用户具有指定的权限,有则返回true,否则返回false
-
第一步:配置类指定权限
// 表示当前用户访问这个路径必须有admin权限
.antMatchers("/user/index").hasAuthority("admin") -
第二步:返回User对象里面设置当前登录对象相对应的权限
// 给予当前用户admin的权限
List<GrantedAuthority> authorities = AuthorityUtils.commaSeparatedStringToAuthorityList("admin");
return new User("username",new BCryptPasswordEncoder().encode("password"),authorities);
注意:此方法只能针对一个权限
2、hasAnyAuthority方法:当前用户具有指定权限的任意一种则返回true,否则返回false
-
配置类指定权限
// 只要有admin或root任意一个权限就能访问
.antMatchers("/user/index").hasAnyAuthority("admin","root")
3、hasRole方法:当前用户具有指定角色才能允许访问
-
第一步:配置类配置
// 当前登录用户必须拥有manage这个角色才能访问
.antMatchers("/user/index").hasRole("manage") -
第二步:hasRole()原码分析:
public ExpressionInterceptUrlRegistry hasRole(String role) {
return access(ExpressionUrlAuthorizationConfigurer.hasRole(role));
}
// 可以看出源码返回角色时默认加了前缀ROLE_
private static String hasRole(String role) {
Assert.notNull(role, "role cannot be null");
Assert.isTrue(!role.startsWith("ROLE_"),
() -> "role should not start with 'ROLE_' since it is automatically inserted. Got '" + role + "'");
return "hasRole('ROLE_" + role + "')";
} -
第三步:给登录角色配置权限
// 因为原码在角色的前面加了前缀ROLE_,所以我们在分配角色的时候也需要加上这个前缀
List<GrantedAuthority> authorities = AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_manage");
return new User(users.getUsername(),encoder.encode(users.getPassword()),authorities);
4、hasAnyRole方法:拥有任意一个角色才能访问
-
第一步:配置类配置
// 当前登录用户拥有manage或admin任意一个角色才能访问
.antMatchers("/user/index").hasAnyRole("admin","manage") -
第二步:给登录角色配置权限
List<GrantedAuthority> authorities = AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_admin");
return new User(users.getUsername(),encoder.encode(users.getPassword()),authorities);
4、自定义403没有权限访问页面
1、配置类中配置
5、常用注解的使用
1、@Secured("ROLE_role1,ROLE_role1")
指定角色才能访问,使用在Controller的方法上
注意:使用该注解必须在主启动类上添加@EnableGlobalMethodSecurity(securedEnabled = true)
2、@PreAuthorize("hasAnyAuthority('menu:system')")
注解进入方法前的权限验证,@PreAuthorize可以将登录用户的角色或权限传入方法中
注意:使用该注解必须在主启动类上添加@EnableGlobalMethodSecurity(prePostEnabled = true)
3、@PostAuthorize("hasAnyAuthority('menu:system')")
这个注解用的并不多,在方法执行后进行权限验证,适合验证带有返回值权限
注意:使用该注解必须在主启动类上添加@EnableGlobalMethodSecurity(prePostEnabled = true)
4、@PostFilter("filterObject.username == 'admin1'")
权限验证之后对返回的数据进行过滤,留下的是admin1的数据
表达式中filterObject引用的是方法返回值List中的某一个元素
5、@PreFilter("filterObject.id % 2 == 0")
权限验证之前对传入的数据进行过滤,当id是2的倍数的时候则留下
6、用户注销
1、配置类中配置
7、基于数据库实现“记住我”
1、原理:
浏览器端:Cookie 储存 加密串
数据库端:加密串 与 用户信息字符串对应
认证时:Cookie 使用 加密串匹配用户信息进行认证
1、第一步:创建数据库表
CREATE TABLE persistent_logins (
username VARCHAR ( 64 ) NOT NULL,
series VARCHAR ( 64 ) PRIMARY KEY,
token VARCHAR ( 64 ) NOT NULL,
last_used TIMESTAMP NOT NULL)
2、配置类中进行配置:注入数据源,配置操作数据库对象
3、配置类进一步配置,设置记住我功能
关键代码:
// 记住我有效时长,以秒为单位 .tokenValiditySeconds(60) .userDetailsService(userDetailsService) // 关闭csrf防护 .and().csrf().disable();
4、登录页面添加=="记住我"==复选框
记住我:<input type="checkbox" name="remember-me" title="记住我" />
注意:name必须为remember-me
8、CSRF理解
什么是CSRF?
CSRF(Cross-site request forgery),中文名称:跨站请求伪造,也被称为:one click attack/session riding,缩写为:CSRF/XSRF。