文章目录
SpringSecurity概述
Spring Security 是针对Spring项目的安全框架,也是Spring Boot底层安全模块默认的技术选型,他可以实现强大的Web安全控制,对于安全控制,我们仅需要引入 spring-boot-starter-security 模块,进行少量的配置,即可实现强大的安全管理!
需要牢记的几个类:
- WebSecurityConfigurerAdapter: 自定义Security策略
- AuthenticationManagerBuilder: 自定义认证策略
- @EnableWebSecurity: 开启WebSecurity模式
Spring Security的两个主要目标是 “认证” 和 “授权”(访问控制)
“授权” (Authorization)
授权发生在系统成功验证您的身份后,最终会授予您访问资源(如信息,文件,数据库,资金,位置,几乎任何内容)的完全权限。
这个概念是通用的,而不是只在Spring Security 中存在。
“认证”(Authentication)
身份验证是关于验证您的凭据,如用户名/用户ID和密码,以验证您的身份。
身份验证通常通过用户名和密码完成,有时与身份验证因素结合使用。
框架核心组件
核心组件
- SecurityContextHolder: 提供对SecurityContext的访问
- SecurityContext: 持有Authentication对象和其他可能需要的信息
- AuthenticationManager: 其中可以包含多个AuthenticationProvider
- ProviderManager: 对象为AuthenticationManager接口的实现类
- AuthenticationProvider: 主要用来进行认证操作的类 调用其中的authenticate()方法去进行认证操作
- Authentication: Spring Security方式的认证主体
- GrantedAuthority: 对认证主题的应用层面的授权,含当前用户的权限信息,通常使用角色表示
- UserDetails:构建Authentication对象必须的信息,可以自定义,可能需要访问DB得到
- UserDetailsService:通过username构建UserDetails对象,通过loadUserByUsername根据userName获取UserDetail对象 (可以在这里基于自身业务进行自定义的实现 如通过数据库,xml,缓存获取等)
过滤器总结
- WebAsyncManagerIntegrationFilter
- SecurityContextPersistenceFilter
- HeaderWriterFilter
- CorsFilter
- LogoutFilter
- RequestCacheAwareFilter
- SecurityContextHolderAwareRequestFilter
- AnonymousAuthenticationFilter
- SessionManagementFilter
- ExceptionTranslationFilter
- FilterSecurityInterceptor
- UsernamePasswordAuthenticationFilter
- BasicAuthenticationFilter
[外链图片转存中…(img-Y2qV9S3F-1592456960227)]
SpringSecurity测试环境部署
导入依赖
导入 thymeleaf依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
在application.properties 中关闭 thymeleaf 缓存
spring.thymeleaf.cache=false
创建静态页面
创建过个静态页面供SpringSecurity控制访问
分别在template目录下创建文件夹 views,在views中创建三个 level01、level02、level03包,然后分别在每个包下创建三个静态页面1.html、2.html、3.html
然后在每个静态页面填写不同的内容加以区分
1.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>level01-1.html</h1>
<hr>
<a th:href="@{/index}">返回主页</a>
</body>
</html>
创建 index.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>hello SpringSecurity</h1>
<hr>
<div style="text-align: center; margin-top: 100px">
<div>
<p>
<a th:href="@{/level01/1}">level 01-1</a>
<a th:href="@{/level01/2}">level 01-2</a>
<a th:href="@{/level01/3}">level 01-3</a>
</p>
</div>
<div>
<p>
<a th:href="@{/level02/1}">level 02-1</a>
<a th:href="@{/level02/2}">level 02-2</a>
<a th:href="@{/level02/3}">level 02-3</a>
</p>
</div>
<div>
<p>
<a th:href="@{/level03/1}">level 03-1</a>
<a th:href="@{/level03/2}">level 03-2</a>
<a th:href="@{/level03/3}">level 03-3</a>
</p>
</div>
</div>
</body>
</html>
创建控制器
创建Controller 实现对各个页面的跳转操作
package com.jason.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* @authot jason_yan
* @date 2020/6/18-9:08
*/
@Controller
public class IndexController {
@RequestMapping({"/","/index"})
public String index(){
return "index";
}
@RequestMapping("/level01/{id}")
public String level1(@PathVariable("id") int id){
return "views/level01/"+id;
}
@RequestMapping("/level02/{id}")
public String level2(@PathVariable("id") int id){
return "views/level02/"+id;
}
@RequestMapping("/level03/{id}")
public String level3(@PathVariable("id") int id){
return "views/level03/"+id;
}
}
Springboot整合SpringSecurity
导入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
此时启动服务器时,SpringSecurity 会拦截所有的请求,用户必须登录SpringSecurity 的 默认登录页面,才可以访问其他资源
账号:user
密码:控制台自动生成
创建SpringSecurity配置类
SecurityConfig配置类继承WebSecurityConfigurerAdapter
重写两个方法:
configure(HttpSecurity http):进行用户授权(验证某个用户是否有权限执行某个操作)
configure(AuthenticationManagerBuilder auth):进行用户认证(检测登录用户是否合法)
在配置类上添加 @EnableWebSecurity的作用:
1: 加载WebSecurityConfiguration配置类, 配置安全认证策略。
2: 加载AuthenticationConfiguration, 配置了认证信息。
package com.jason.controller.config;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
/**
* @authot jason_yan
* @date 2020/6/18-9:54
*/
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// 设置请求资源的权限(授权)
http
.authorizeRequests() // 对请求进行授权
.antMatchers("/").permitAll() // 设置首页任何人都可访问
.antMatchers("/level01/**").hasRole("doctor") // 指定要设置权限信息的地址和授权角色
.antMatchers("/level02/**").hasRole("master") // 两个*代表目录的深度(任意深度)
.antMatchers("/level03/**").hasRole("teacher")
.and()
.formLogin(); // 设置未授权的请求跳转 到 登录表单页面
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
// 模拟假数据
auth.inMemoryAuthentication()
.withUser("zs").password("111").roles("doctor")
.and()
.withUser("ls").password("222").roles("master")
.and()
.withUser("ww").password("333").roles("teacher");
}
}
测试以上配置:
1、当请求首页上的链接时,会跳转到SpringSecurity 默认登录页面,因为我们没有相应的权限访问该资源————.formLogin() 实现本次跳转
2、可以使用 zs、ls、ww等用户进行登录
3、登录成功后无法访问相对应的角色资源:报错 java.lang.IllegalArgumentException: There is no PasswordEncoder mapped for the id “null”
解决上述问题:在登录过程中,SpringSecurity处理的密码不能是明文密码,所以我们的密码需要 在认证方法中 进行对密码进行加密
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
// 模拟假数据
auth.inMemoryAuthentication()
.passwordEncoder(bCryptPasswordEncoder) // 设置加密方式
.withUser("zs").password(bCryptPasswordEncoder.encode("111")).roles("doctor")
.and()
.withUser("ls").password(bCryptPasswordEncoder.encode("222")).roles("master")
.and()
.withUser("ww").password(bCryptPasswordEncoder.encode("333")).roles("teacher");
}
以上即可实现相应的用户 访问相应的资源
设置用户访问权限不足的跳转页面
在SecurityConfig配置类的configure(HttpSecurity http)方法中
添加 当用户权限不足时 的访问路径
@Override
protected void configure(HttpSecurity http) throws Exception {
// 设置请求资源的权限(授权)
http
.authorizeRequests() // 对请求进行授权
...
.formLogin(); // 设置未授权请求跳转的登录表单页面
http.exceptionHandling().accessDeniedPage("/no/auth"); // 用有访问权限的角色被访问拒绝后跳转的路径
}
控制器 实现跳转
@RequestMapping("/no/auth")
public String noAuth(){
return "views/no-auth";
}
在views目录下创建 跳转的页面 no-auth.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>您的权限不足以访问此资源</h1>
<hr>
<a th:href="@{/index}">返回主页</a>
</body>
</html>
注销功能
在 SecurityConfig 配置类的 configure(HttpSecurity http) 方法中
开启注销功能
@Override
protected void configure(HttpSecurity http) throws Exception {
...
http.logout() //开启用户退出登录的功能
.logoutSuccessUrl("/"); //设置成功退出登录后前往首页
// 下方无代码
}
在index.html页面中添加 退出登录的请求链接地址
<body>
<h1>hello SpringSecurity</h1>
<hr>
<p style="text-align: right; margin-right: 20px">
<a th:href="@{/logout}">退出登录</a>
</p>
<div style="text-align: center; margin-top: 100px">
...
</div>
</body>
</html>
SpringSecurity 整合 Thymeleaf
需要实现的功能:
1、当用户未登录时——显示登录按钮
2、当用户登录时——显示退出登录按钮
3、当用户登录至页面时只显示相对应的权限链接
导入依赖
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity5</artifactId>
<version>3.0.4.RELEASE</version>
</dependency>
添加首页命名空间:xmlns:sec=“http://www.thymeleaf.org/thymeleaf-extras-springsecurity5”
并 修改 index.html 页面
要实现此功能必须关闭csrf功能:跨站请求伪造
在 SecurityConfig 配置类的 configure(HttpSecurity http) 方法中
@Override protected void configure(HttpSecurity http) throws Exception { ... http.csrf().disable(); // 下方无代码 }
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>hello SpringSecurity</h1>
<hr>
<!--未登录的情况-->
<div sec:authorize="!isAuthenticated()">
<p style="text-align: right; margin-right: 20px">
<a th:href="@{/login}">登录</a>
</p>
</div>
<!--用户已登录-->
<div sec:authorize="isAuthenticated()">
<!--显示用户名和角色-->
name:<span sec:authentication="principal.username"></span>
role:<span sec:authentication="principal.authorities"></span>
<!--显示退出登录按钮-->
<p style="text-align: right; margin-right: 20px">
<a th:href="@{/logout}">退出登录</a>
</p>
</div>
...
</body>
</html>
实现当用户登录至页面时只显示相对应的权限链接
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>hello SpringSecurity</h1>
<hr>
<!--未登录的情况-->
<div sec:authorize="!isAuthenticated()">
<p style="text-align: right; margin-right: 20px">
<a th:href="@{/toLogin}">登录</a>
</p>
<div style="text-align: center; margin-top: 100px">
<div>
<p>
<a th:href="@{/level01/1}">level 01-1</a>
<a th:href="@{/level01/2}">level 01-2</a>
<a th:href="@{/level01/3}">level 01-3</a>
</p>
</div>
<div>
<p>
<a th:href="@{/level02/1}">level 02-1</a>
<a th:href="@{/level02/2}">level 02-2</a>
<a th:href="@{/level02/3}">level 02-3</a>
</p>
</div>
<div>
<p>
<a th:href="@{/level03/1}">level 03-1</a>
<a th:href="@{/level03/2}">level 03-2</a>
<a th:href="@{/level03/3}">level 03-3</a>
</p>
</div>
</div>
</div>
<!--用户已登录-->
<div sec:authorize="isAuthenticated()">
<div>
<!--显示用户名和角色-->
name:<span sec:authentication="principal.username"></span>
role:<span sec:authentication="principal.authorities"></span>
<!--显示退出登录按钮-->
<p style="text-align: right; margin-right: 20px">
<a th:href="@{/logout}">退出登录</a>
</p>
</div>
<div style="text-align: center; margin-top: 100px">
<div sec:authorize="hasRole('doctor')">
<p>
<a th:href="@{/level01/1}">level 01-1</a>
<a th:href="@{/level01/2}">level 01-2</a>
<a th:href="@{/level01/3}">level 01-3</a>
</p>
</div>
<div sec:authorize="hasRole('master')">
<p>
<a th:href="@{/level02/1}">level 02-1</a>
<a th:href="@{/level02/2}">level 02-2</a>
<a th:href="@{/level02/3}">level 02-3</a>
</p>
</div>
<div sec:authorize="hasRole('teacher')">
<p>
<a th:href="@{/level03/1}">level 03-1</a>
<a th:href="@{/level03/2}">level 03-2</a>
<a th:href="@{/level03/3}">level 03-3</a>
</p>
</div>
</div>
</div>
</body>
</html>
记住我功能
只要登录之后,关闭浏览器,依然可以保留登录状态
原因:
登录成功后,服务器会为用户生成cookie并发送给浏览器保存
以后用户登录会携带此cookie,只要通过检查就可以免登录了
如果点击注销,则会删除相应用户的cookie
//定制请求的授权规则
@Override
protected void configure(HttpSecurity http) throws Exception {
...
http.rememberMe(); // 记住我
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PIz17zO3-1592456716357)(F:\项目学习\SpringBoot\springboot Security\image-20200618115148750.png)]
自定义登录页面
在views目录下创建登录页面 login.html
PS:登录表单的提交方式必须是 post
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form th:action="@{/do/login}" style="text-align: center; margin-top: 100px" method="post">
<h1>用户登录页面</h1>
<hr>
<p>username:<input type="text" name="username" placeholder="用户名"/></p>
<p>password:<input type="password" name="password" placeholder="密码"/></p>
<p><input type="checkbox" name="remember"> 记住我
<input type="submit" value="登录"></input>
</p>
</form>
</body>
</html>
修改SecurityConfig 配置类的 configure(HttpSecurity http) 方法
protected void configure(HttpSecurity http) throws Exception {
// 设置请求资源的权限(授权)
http
...
.formLogin() // 设置未授权请求跳转的登录表单页面
.loginPage("/toLogin").permitAll() // 指定登录页面地址且所有人都可以访问
.usernameParameter("username") // 告诉SpringSecurity登录账号请求参数名(与表单一致)
.passwordParameter("password") // 告诉SpringSecurity登录密码请求参数名(与表单一致)
.loginProcessingUrl("/do/login") // 登录表单提交地址
;
...
http.rememberMe().rememberMeParameter("remember"); //开启记住我功能,参数与表单一致; // 记住我
}
控制器实现跳转
@RequestMapping("/toLogin")
public String toLogin(){
return "views/login";
}
SecurityConfig 配置类 全部代码
package com.jason.controller.config;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
/**
* @authot jason_yan
* @date 2020/6/18-9:54
*/
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// 设置请求资源的权限(授权)
http
.authorizeRequests() // 对请求进行授权
.antMatchers("/").permitAll() // 设置首页任何人都可访问
.antMatchers("/level01/**").hasRole("doctor") // 指定要设置权限信息的地址和授权角色
.antMatchers("/level02/**").hasRole("master") // 两个*代表目录的深度(任意深度)
.antMatchers("/level03/**").hasRole("teacher")
.and()
.formLogin() // 设置未授权请求跳转的登录表单页面
.loginPage("/toLogin").permitAll() // 指定登录页面地址且所有人都可以访问
.usernameParameter("username") // 告诉SpringSecurity登录账号请求参数名(与表单一致)
.passwordParameter("password") // 告诉SpringSecurity登录密码请求参数名(与表单一致)
.loginProcessingUrl("/do/login") // 登录表单提交地址
;
http.exceptionHandling().accessDeniedPage("/no/auth"); // 用有访问权限的角色被访问拒绝后跳转的路径
http.logout() //开启用户退出登录的功能
.logoutSuccessUrl("/"); //设置成功退出登录后前往主页
http.csrf().disable(); // 关闭跨站请求伪造
http.rememberMe().rememberMeParameter("remember"); //开启记住我功能,参数与表单一致; // 记住我
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
// 模拟假数据
auth.inMemoryAuthentication()
.passwordEncoder(bCryptPasswordEncoder) // 设置加密方式
.withUser("zs").password(bCryptPasswordEncoder.encode("111")).roles("doctor")
.and()
.withUser("ls").password(bCryptPasswordEncoder.encode("222")).roles("master")
.and()
.withUser("ww").password(bCryptPasswordEncoder.encode("333")).roles("teacher");
}
}
SpringSecurity 整合 数据库
待续…