目录
2.1.1 FilterSecurityInterceptor过滤器
2.1.2 ExceptionTranslationFilter过滤器
2.1.3 UsernamePasswordAuthenticationFilter过滤器
2.2.3 WebSecurityConfigurer配置类
一、简介
Spring 是非常流行和成功的 Java 应用开发框架,Spring Security 正是 Spring 家族中的成员。Spring Security 基于 Spring 框架,提供了一套 Web 应用安全性的完整解决方案。
Spring Security是一个基于Spring框架的安全性框架,它提供了一系列的安全性服务和配置选项,帮助开发者在其应用程序中实现安全认证和访问控制。
1.1 概要内容
分为两个主要区域:
(1)用户认证:验证某个用户是否为系统中的合法主体,也就是说用户能否访问该系统。用户认证一般要求用户提供用户名和密码。系统通过校验用户名和密码来完成认证过程。通俗点说就是系统认为用户是否能登录。
(2)用户授权:指的是验证某个用户是否有权限执行某个操作。在一个系统中,不同用户
所具有的权限是不同的。一般来说,系统会为不同的用户分配不同的角色,而每个角色则对应一系列的权限。比如:超级管理员可以对信息进行修改,而普通用户只能读取。通俗点讲用户授权就是系统判断用户是否有权限去做某些事情。
1.2 对比
(1)SpringSecurity特点
-
安全性:Spring Security是一个以安全性为中心的框架,它提供了很多内置的安全功能和认证机制,可以保护应用程序免受常见的安全威胁。
-
灵活性:Spring Security非常灵活,可以根据应用程序的需求进行配置和扩展。它允许开发者自定义认证和授权策略、使用不同的认证方法和机制,还可以自定义过滤器和拦截器等等。
-
高可扩展性:Spring Security可以无缝地集成到Spring应用程序中,并与其他Spring组件一起工作。开发者可以使用Spring的依赖注入和AOP等功能来扩展Spring Security的功能。
-
支持多种认证机制:Spring Security支持许多常见的认证方法和机制,如LDAP、OpenID、HTTP基本认证、表单认证和CAS等等。
-
支持方法级别和资源级别的授权:Spring Security可以通过注释或配置来实现方法级别和资源级别的授权控制,以确保只有授权用户可以访问受保护的资源。
-
开发者友好:Spring Security提供了很多工具和文档,可以帮助开发者快速、轻松地使用和配置Spring Security。同时,它还提供了很多可重用的安全模块,可以降低开发的复杂性和工作量。
-
专门为Web设计
-
重量级
(2)Shiro特点
-
易于使用:Shiro使用起来非常简单,它的API设计良好且易于理解,开发者可以快速上手使用,不需要太多的安全技能或知识。
-
多种认证方式:Shiro支持多种认证方式,如基于表单的认证、基于HTTP Basic认证、OAuth认证、JWT认证等等,可以满足不同应用的不同认证需求。
-
多种授权策略:Shiro支持多种授权策略,可以配置基于角色、权限、资源和行为的授权策略,同时还支持限制同一个用户的并发登录数限制、黑名单和白名单等高级授权策略。
-
高度可定制化:Shiro允许开发者使用自定义的Realm、Session DAO、Cache等组件,可以满足各种调整和配置需求。
-
安全领域常用功能:Shiro支持许多开发人员常见的安全功能,包括密码加密、角色、权限、会话管理和认证日志等功能。
-
集成性:Shiro能够与多种框架和技术集成,如Spring、MyBatis、Apache Velocity、Apache Camel和Apache Shale等,支持各种应用场景。
-
不局限于Web环境
-
特定的需求需要手动编码
-
轻量级
(3)SpringSecurity和Shiro对比
Spring Security和Apache Shiro都是流行的安全框架,它们都提供了认证和授权的功能,可以保证应用程序的安全性。两者有着不同的特点和优缺点:
1. 架构:Spring Security是一个基于Spring Framework的安全框架,而Shiro是一个独立的安全框架。
2. 功能:两者都提供了认证和授权的功能,Spring Security的授权功能更加强大和灵活,支持更多的授权设定方式;Shiro的API设计更加简单易用。
3. 性能:Shiro相对于Spring Security来说更加轻量级,可以更好地适应小型项目。
4. 项目支持:Spring Security由Spring社区提供支持,开源社区比较活跃,提供了很多代码示例和文档;而Shiro社区相对较小,文档和示例较少。
5. 生态圈:Spring Security与Spring Framework的集成更加无缝,可以更好地利用Spring框架的依赖注入等功能。而Shiro可以与多种技术集成,其独立性和简单性使得它更加易于集成。
综上所述,选择使用哪一个安全框架还需要根据具体的项目需求进行评估。如果开发人员需要更多的授权设定方式,以及更好的生态支持,则选择Spring Security更为合适;而如果开发人员更关注轻量、易用和可集成性,则选择Shiro更为合适。
因此,一般来说,常见的安全管理技术栈的组合是这样的:
• SSM + Shiro
• Spring Boot/Spring Cloud + Spring Security
以上只是一个推荐的组合而已,如果单纯从技术上来说,无论怎么组合,都是可以运行的。
二、SpringSecurity基本原理
SpringSecurity本质是一个过滤器链,有很多过滤器:
org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFil
ter
org.springframework.security.web.context.SecurityContextPersistenceFilter
org.springframework.security.web.header.HeaderWriterFilter
org.springframework.security.web.csrf.CsrfFilter
org.springframework.security.web.authentication.logout.LogoutFilter
org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter
org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter
org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter
org.springframework.security.web.savedrequest.RequestCacheAwareFilter
org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter
org.springframework.security.web.authentication.AnonymousAuthenticationFilter
org.springframework.security.web.session.SessionManagementFilter
org.springframework.security.web.access.ExceptionTranslationFilter
org.springframework.security.web.access.intercept.FilterSecurityInterceptor
Spring Security的基本原理是通过过滤器链对HTTP请求进行处理,在用户访问受保护的资源之前执行安全验证和授权。其基本原理如下:
1. 过滤器链机制:Spring Security通过FilterChain的机制,将HTTP请求按照顺序传递给一系列的过滤器,这些过滤器用于处理请求的各个阶段,包括安全验证、授权和处理过程中的异常处理等。
2. 用户身份验证:Spring Security通过AuthenticationManager接口对用户的身份进行验证,验证成功返回一个包含用户身份信息的Authentication对象,验证失败则抛出异常。
3. 认证信息管理:在Spring Security中对认证信息进行管理的接口是AuthenticationManager,与之对应的是AuthenticationProvider, 他为AuthenticationManager接口提供了实现,它实现了对用户认证信息的提供,针对不同的认证方式提供相应的认证服务。
4. 授权管理:Spring Security通过AccessDecisionManager接口对用户身份进行授权,针对不同的授权策略,可以通过提供不同的AccessDecisionManager实现来处理授权。AccessDecisionManager需要传入一个包含访问上下文信息和授权信息的SecurityContext对象,返回决策结果。
5. 安全配置:项目启动时,Spring Security会读取配置文件中的安全配置信息,如安全认证方式、授权策略和访问规则等,通过这些配置信息,Spring Security能够对请求的url、资源进行授权控制。
综上所述,Spring Security主要通过过滤器链、AuthenticationManager、AccessDecisionManager等组件对用户进行身份认证和授权,提供了安全认证、授权等基础功能并可以进行灵活的扩展和配置。
2.1 部分过滤器介绍
2.1.1 FilterSecurityInterceptor过滤器
是一个方法级的权限过滤器,基本位于过滤器的最底部
2.1.2 ExceptionTranslationFilter过滤器
是个异常过滤器,用来处理在认证、授权过程中发生的异常。
2.1.3 UsernamePasswordAuthenticationFilter过滤器
对/login的POST请求做拦截,校验表单中用户名、密码。
在该过滤器的attempAuthentication方法中,定义了默认的登录方式:账号密码由SpringSecurity定义生成,输出到控制台。实际项目中,我们的登录会重写这个方法。
那么登录成功和失败的处理在哪里呢?
可以点击这个过滤器的父类:AbstractAuthenticationProcessingFilter过滤器,可以看到里面有两个方法:successfulAuthentication方法和unsuccessfulAuthentication方法。
successfulAuthentication方法对应着登录成功的处理:
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Authentication success. Updating SecurityContextHolder to contain: " + authResult);
}
SecurityContextHolder.getContext().setAuthentication(authResult);
this.rememberMeServices.loginSuccess(request, response, authResult);
if (this.eventPublisher != null) {
this.eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(authResult, this.getClass()));
}
this.successHandler.onAuthenticationSuccess(request, response, authResult);
}
unsuccessfulAuthentication方法对应着登录失败的处理:
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {
SecurityContextHolder.clearContext();
if (this.logger.isDebugEnabled()) {
this.logger.debug("Authentication request failed: " + failed.toString(), failed);
this.logger.debug("Updated SecurityContextHolder to contain null Authentication");
this.logger.debug("Delegating to authentication failure handler " + this.failureHandler);
}
this.rememberMeServices.loginFail(request, response);
this.failureHandler.onAuthenticationFailure(request, response, failed);
}
在实际应用中,我们的登录成功和登录失败会重写以上两个方法。
2.2 两个重要接口介绍
2.2.1 UserDetailsService接口
UserDetailsService接口:查询数据库用户名和密码过程。
当什么也没有配置的时候,账号和密码是由 Spring Security 定义生成的。而在实际项目中
账号和密码都是从数据库中查询出来的。 所以我们要通过自定义逻辑控制认证逻辑。
自定义登录方式,只用继承UsernamePasswordAuthenticationFilter过滤器,重写attempAuthentication方法,传入我们自己数据库查到的用户名和密码。
注意:这个方法只是得到用户名和密码的方法,我们查数据库的方法不写在这里,写在UserDetailsService接口中。用户可以自定义UserDetailsService的实现类,并将它配置到Spring Security框架中,以满足应用程序的特定认证需求。例如,将UserDetailsService与数据库集成,以获取用户的详细信息。
返回值是UserDetails这个类(系统默认的用户主体)
我们可以写一个类实现UserDetails接口,设置用户名和密码信息,也可以使用Spring Security帮我们实现的User类。
public interface UserDetails extends Serializable {
// 表示获取登录用户所有权限
Collection<? extends GrantedAuthority> getAuthorities();
// 表示获取密码
String getPassword();
// 表示获取用户名
String getUsername();
// 表示判断账户是否过期
boolean isAccountNonExpired();
// 表示判断账户是否被锁定
boolean isAccountNonLocked();
// 表示凭证(密码)是否过期
boolean isCredentialsNonExpired();
// 表示当前用户是否可用
boolean isEnabled();
}
UserDetails这个类有一个实现类User
总结过程如下:
- 创建类继承UsernamePasswordAuthenticationFilter过滤器,重写attempAuthentication()方法、successfulAuthentication()方法和unsuccessfulAuthentication()方法。
- 创建类实现UserDetailService接口,编写查询数据过程。返回User对象,这个User对象是安全框架提供对象
2.2.2 PasswordEncoder接口
数据加密接口,可对密码进行加密。用于返回User对象里面密码加密。SpringSecurity只认这个加密,其他不认。
package org.springframework.security.crypto.password;
public interface PasswordEncoder {
// 表示把参数按照特定的解析规则进行解析
String encode(CharSequence var1);
// 表示验证从存储中获取的编码密码与编码后提交的原始密码是否匹配。如果密码匹配则返回true;如果不匹配则返回false。第一个参数代表需要解析的密码,第二个参数代表存储的密码
boolean matches(CharSequence var1, String var2);
// 表示如果解析的密码能够再次进行解析且达到更安全的结果则返回true,否则返回false
default boolean upgradeEncoding(String encodedPassword) {
return false;
}
}
PasswordEncoder接口的一个实现类是BCryptPasswordEncoder 。
BCryptPasswordEncoder 是 Spring Security 官方推荐的密码解析器,平时多使用这个解析
器。
BCryptPasswordEncoder 是对 bcrypt 强散列方法的具体实现。是基于 Hash 算法实现的单
向加密。可以通过 strength 控制加密强度,默认 10.
2.2.3 WebSecurityConfigurer配置类
SpringSecurity提供一个配置类,我们在做配置时需要实现这个配置类。
三、SpringSecurity的使用
首先创建一个SpringBoot项目:
引入SpringSecurity依赖:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
编写controller,如下:
启动后,控制台出现密码:
启动后,访问所编写的接口,跳转到登录页面。(可能访问比较慢,耐心等待)。
输入用户名user,密码是控制台打印的密码
登录成功后即可访问到所写内容。
3.1 设置登录的用户名和密码
方式一:通过配置文件配置
通过以上设置后,控制台不再打印密码,此时登录的用户名和密码信息就是所配置的信息。
通过以上修改后的用户名和密码即可实现登录。
方式二:通过配置类
要通过配置类进行配置,需要继承WebSecurityConfigurer类。在WebSecurityConfigurer类中有个configure方法,我们重写以下方法。
创建一个config包,写一个配置类继承WebSecurityConfigurer,操作如下:
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
/*
* 通过参数auth设置用户名和密码
* BCryptPasswordEncoder是PasswordEncoder接口的实现类,用于对密码进行加密操作
* withUser("qianhan") 传入用户名
* password(password) 传入密码
* roles("admin") 传入角色
* */
BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder();
String password = passwordEncoder.encode("123456");
auth.inMemoryAuthentication().withUser("qianhan").password(password).roles("admin");
}
// 使用在方法上,标注将该方法的返回值存储到 Spring 容器中
@Bean
PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
再对接口进行访问,可输入所配置的账号密码即可登录成功。
方式三:自定义配置实现类
以上两种方式在实际开发中并不实用,因为我们一般都要从数据库中查询到用户信息。在SpringSecurity中,会先到配置文件和配置类中找用户信息,如果找不到就会去找UserDetailsService接口,从这找到账号密码。
我们的自定义实现类就需要写一个类实现UserDetailsService这个接口。
步骤:
Step1:创建配置类,设置使用哪个UserDetailsService实现类
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
//Step1:创建配置类,设置要使用哪个userDetailsService实现类
// 我们后面需要自己写实现类注入进来
@Autowired
private UserDetailsService userDetailsService;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//传入UserDetailsService实现类和密码加密操作
auth.userDetailsService(userDetailsService).passwordEncoder(password());
}
//BCryptPasswordEncoder是PasswordEncoder接口的实现类,用于对密码进行加密操作
@Bean
PasswordEncoder password(){
return new BCryptPasswordEncoder();
}
}
Step2:编写实现类,返回User对象,User对象有用户名、密码和操作权限
@Service("userDetailsService") //注意,这个名字要和前面的SecurityConfig中注入UserDetailsService类的名字相同
public class MyUserDetailsService implements UserDetailsService {
//UserDetails:系统默认的用户主体
@Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
//Step2:编写实现类,返回User对象,User对象有用户名、密码和操作权限
/**
* 这里通过查看User可以看到其构造方法中,需要传入用户名、密码、权限
* public User(String username, String password, Collection<? extends GrantedAuthority> authorities) {
* this(username, password, true, true, true, true, authorities);
* }
* 其中,权限是一个GrantedAuthority类型集合,其工具类是AuthorityUtils
* 强调:auths不能传null,必须有值,如果auths为null会报错:
* org.springframework.security.authentication.InternalAuthenticationServiceException: Cannot pass a null GrantedAuthority collection
* 在实际开发中,我们通过查询数据库得到账号密码和权限
* 如果查不到,则抛出异常。查得到就返回数据
*
*/
//传入用户名、密码、权限
List<GrantedAuthority> auths = AuthorityUtils.commaSeparatedStringToAuthorityList("admin");
return new User("lisi",new BCryptPasswordEncoder().encode("123456"),auths);
}
}
通过所写的用户名和密码即可登录访问
强调:在创建User对象时,权限不能为null。
如果权限传入为null,会报以下错误:
登录页面报错如下:
四、实现数据库认证来完成用户登录
综合以上内容,整合数据库进行操作。
4.1 项目准备
(1)创建数据库和数据库表
(2)在配置文件中配置数据库信息
(3)引入依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--mybatis-plus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.0.5</version>
</dependency>
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--lombok 用来简化实体类-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
(4) 创建实体类
创建一个entity包,在包里面创建实体类
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Users {
private Integer id;
private String username;
private String password;
}
(5)创建mapper
@Mapper
public interface UsersMapper extends BaseMapper<Users> {
}
(5) 创建配置类继承WebSecurityConfigurerAdapter
(跟前文一样)
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
//Step1:创建配置类,设置要使用哪个userDetailsService实现类
// 我们后面需要自己写实现类注入进来
@Autowired
private UserDetailsService userDetailsService;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//传入UserDetailsService实现类和密码加密操作
auth.userDetailsService(userDetailsService).passwordEncoder(password());
}
//BCryptPasswordEncoder是PasswordEncoder接口的实现类,用于对密码进行加密操作
@Bean
PasswordEncoder password(){
return new BCryptPasswordEncoder();
}
}
(5) 创建service
创建MyUserDetailsService类,实现UserDetailsService接口。
调用mapper方法查询数据库进行用户认证:
@Service("userDetailsService")
public class MyUserDetailsService implements UserDetailsService {
/**
* 步骤:
* 1. 根据用户名到数据库中查找用户Users(我们自己写的实体类)
* 2. 用户信息不存在,返回异常信息
* 3. 封装用户角色权限信息(此处是写死的,没有从数据库里面取)
* 4. 封装到User中。(Spring Security的类)
*/
@Autowired
private UsersMapper usersMapper;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//调用usersMapper方法,根据用户名查询数据库
QueryWrapper<Users> wrapper = new QueryWrapper<>();
wrapper.eq("username",username);
Users users = usersMapper.selectOne(wrapper); // 数据库里面存储的用户信息
//判断。如果查不到,则抛出异常
if (users == null){
throw new UsernameNotFoundException("用户名不存在");
}
//这里的角色信息没有从数据库里查询。实际开发中要从数据库里查询
List<GrantedAuthority> auths = AuthorityUtils.commaSeparatedStringToAuthorityList("admin");
return new User(users.getUsername(),new BCryptPasswordEncoder().encode(users.getPassword()),auths);
}
}
五、权限控制与自定义页面
5.1 自定义登录页面
Step1:在配置类实现相关的配置
在前文,我们已经写了一个SecurityConfig配置类继承WebSecurityConfigurerAdapter类,并实现了configure(AuthenticationManagerBuilder auth)方法。
这里,我们实现的是另一个方法configure(HttpSecurity http)方法。
//设置要跳转自定义登录页面
@Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin() //自定义自己编写的登录页面
.loginPage("/login.html") //登录页面设置
.loginProcessingUrl("/user/login") //登录访问路径
.defaultSuccessUrl("/test/index").permitAll() //登录成功之后
.and().authorizeRequests() //哪些可以访问,哪些不可以
.antMatchers("/","/test/hello","/user/login").permitAll() //设置哪些路径不需要验证
.anyRequest().authenticated() //所有请求都可以访问
.and().csrf().disable(); //关闭csrf防护
}
Step2:创建controller方法
通过以上配置后,该接口只有登录后才能访问。
@GetMapping("index")
public String index(){
return "hello index";
}
Step3:创建login.html页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/user/login" method="post">
用户名:<input type="text" name="username">
<br>
密码:<input type="password" name="password">
<br>
<input type="submit" value="login">
</form>
</body>
</html>
启动后,访问前文所写的test/hello接口可以访问,访问test/index时,跳转到我们定义的登录页面中,输入账号密码登录成功后即可访问到页面。
5.2 基于角色或权限的访问控制
(1)第一个方法:hasAuthority()方法
如果当前的主体具有指定的权限,则返回true,否则返回false。
结合前文在MyUserDetailsService的登录方法,我们给用户赋予权限,比如admin角色。
Spring Security就会根据配置类中的设置来判断当前角色是否有访问的权限。
举例:
我们在配置类中加上以下信息:
在MyUserDetailsService类中,写明用户的角色。
重启后,访问test/index接口,发现可以登录成功并访问到“hello index”
如果我们将当前用户的角色信息改成"student",如下:
发现页面登录后报403,即没有访问的权限。
注意:hasAuthority()方法只能有一个权限,但是如果该接口可以有多个角色可以访问的权限,(比如该管理员可以访问,学生也可以访问)就无法进行设置。就需要用到hasAnyAuthority()方法
(2)第二个方法:hasAnyAuthority()方法
与hasAuthority()方法一样,如果当前的主体有所指定的角色,则返回true,否则返回false。但是hasAnyAuthority()方法可以指定多个角色访问权限。
备注:多个角色可以像上面写法,用逗号隔开传多个字符串,也可以以下面这种写法:
总结:以上两个方法的源码如下:
(3)第三个方法:hasRole()方法
该方法与前文的hasAuthority()类似,只能写一个角色权限。但是有一个使用上的区别。
首先,还是在配置类中进行配置
之后这个步骤与前面的hasAuthority()有所区别,
在用户角色中,要给角色信息加上"ROLE_",否则无法识别到该角色信息
(4)第四个方法:hasAnyRole()方法
该方法与前文的hasAnyAuthority()类似,可以写多个角色权限。同样有一个使用上的区别。
在用户角色中,要给角色信息加上"ROLE_",否则无法识别到该角色信息
总结:以上两个方法的源码如下:
5.3 自定义403页面
在前文测试中,我们可以看到,如果用户没有访问权限,会跳到一个403页面。在实际开发中,我们想让它跳转到一个我们写的403页面,可以进行以下配置:
Step1:自定义一个页面
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>403---别想进!你没有访问权限!</h1>
</body>
</html>
Step2:在配置类中进行以下配置:
Step3:跳转结果如下
六、注解开发
6.1 @Secured
用户具有某个角色才能访问到该接口
Step1:开启注解
可以在配置类上加上注解,也可以在启动类上加上注解
小补充:如果在配置类和启动类都加上了这个注解,会报以下错误:
Description:
The bean 'metaDataSourceAdvisor' could not be registered. A bean with that name has already been defined and overriding is disabled.
Step2:定义接口加上@Secured
Step3:设置用户角色
6.2 @PreAuthorize
该注解适合进入方法前的权限验证。
@PreAuthorize 可以将登录用户的 roles/permissions 参数传到方法中
Step1:开启注解
Step2:定义接口加上@PreAuthorize
Step3:设置用户角色
6.3 @PostAuthorize
该注解使用并不多,在方法执行后再进行权限验证,适合验证带有返回值的权限。
Step1:开启注解
Step2:定义接口加上@PostAuthorize
Step3:设置用户角色
结果:
在方法执行之后,跳转到403页面
6.4 @PostFilter
对方法返回数据进行过滤。例子如下:我们对"aaa"这条返回数据进行过滤。
通过访问得到以下结果:
6.5 @PreFilter
传入方法数据进行过滤,即进入控制器之前对数据进行过滤。
七、 用户注销
Step1:在登录页面加一个退出登录
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
登录成功!
<a href="/logout">退出</a>
</body>
</html>
Step1:在配置类中配置
将登录成功后跳转页面设置为success.html
加上以下信息配置退出登录
八、“记住我”功能
在以前我们可以采用cookie技术实现,这里我们使用安全框架机制实现自动登录
8.1 实现原理
(图片来源:尚硅谷的笔记)
8.2 具体实现
Step1:创建数据库表
也可以不创建,Spring Security也可以帮我们创建
有兴趣可以看以下源码:
我们自定义一张表:
Step2:配置类
首先,注入数据源
其次,在前文的http.formLogin()后面加上以下配置
Step3:在登录页面加上记住我
最后实现“记住我”功能,并且表中会多一条数据
九、CSRF
跨站请求伪造(英语:Cross-site request forgery),也被称为 one-click attack 或者 session riding,通常缩写为 CSRF 或者 XSRF, 是一种挟制用户在当前已登录的 Web 应用程序上执行非本意的操作的攻击方法。
从 Spring Security 4.0 开始,默认情况下会启用 CSRF 保护,以防止 CSRF 攻击应用序,Spring Security CSRF 会针对 PATCH,POST,PUT 和 DELETE 方法进行防护。