9.SpringBoot整合SpringSecurity
9.1 SpringSecurity介绍
什么是SpringSecurity?
- Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架。
- 它提供了一组可以在Spring应用上下文中配置的Bean,充分利用了Spring IoC,DI(控制反转Inversion of Control ,DI:Dependency Injection 依赖注入)和AOP(面向切面编程)功能,为应用系统提供声明式的安全访问控制功能,减少了为企业系统安全控制编写大量重复代码的工作。
SpringSecurity功能
- Spring Security对Web安全性的支持大量地依赖于Servlet过滤器。这些过滤器拦截进入请求,并且在应用程序处理该请求之前进行某些安全处理。 Spring Security提供有若干个过滤器,它们能够拦截Servlet请求,并将这些请求转给认证和访问决策管理器处理,从而增强安全性。根据自己的需要,可以使用适当的过滤器来保护自己的应用程序。
- FilterToBeanProxy是一个特殊的Servlet过滤器,它本身做的工作并不多,而是将自己的工作委托给Spring应用程序上下文 中的一个Bean来完成。被委托的Bean几乎和其他的Servlet过滤器一样,实现javax.servlet.Filter接 口,但它是在Spring配置文件而不是web.xml文件中配置的。
SpringSecurity最关键的两个点是
- 第一个被称为“认证”,是为用户建立一个他所声明的主体。主体一般是指用户,设备或可以在系统中执行动作的其他系统。
- 第二个叫“授权”,指的是一个用户能否在应用中执行某个操作,在到达授权判断之前,身份的主体已经由身份验证过程建立。
SpringSecurity关键类
- WebSecurityConfigurerAdapter:自定义Security策略
- AuthenticationManagerBuilder:自定义认证策略
- @EnableWebSecurity:开启WebSecurity模式
9.2 SpringSecurity整合Springboot案例
第一步:搭建环境
- 首先创建一个SpringBoot项目勾选web和Thymeleaf模块和SpringSecurity安全模块
- 其次导入静态资源,以及页面(目录结构如下)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rLUC5ozA-1595675245457)
-
静态资源来源 https://www.bilibili.com/video/BV1PE411i7CV?p=35
-
编写controller层访问首页
@Controller
public class RouterController {
@RequestMapping({"/","/index"})
public String index(){
return "index";
}
@RequestMapping("/login")
public String login(){
return "views/login";
}
@RequestMapping("/level1/{id}")
public String level1(@PathVariable("id") Integer id){
return "views/level1/"+id;
}
@RequestMapping("/level2/{id}")
public String level2(@PathVariable("id")Integer id){
return "views/level2/"+id;
}
@RequestMapping("/level3/{id}")
public String level3(@PathVariable("id")Integer id){
return "views/level3/"+id;
}
}
- 首页展示(现在是没有权限认证的也就是说Level123都可以访问到)
第二步:编写SpringSecurity安全配置
- 主要功能就是授权和认证 要实现的类是WebSecurityConfigurerAdapter(与web安全配置适配的类)
- 授权可以设置权限,设置登录页,设置记住我,设置角色匹配路径
- 认证需要设置用户以及权限密码和密码的编码格式
package com.yingge.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.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
@EnableWebSecurity
public class MySercurityConfig extends WebSecurityConfigurerAdapter {
//授权
@Override
protected void configure(HttpSecurity http) throws Exception {
//配置拦截权限
http
.authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/level1/**").hasRole("vip1")
.antMatchers("/level2/**").hasRole("vip2")
.antMatchers("/level3/**").hasRole("vip3")
.and().formLogin()
.and().rememberMe()
.and().logout().logoutSuccessUrl("/");
//没有权限默认到登录页面 http.formLogin();
//注销 http.logout();
//记住我 http.rememberMe();
//logoutSuccessUrl 退出成功返回首页
}
//认证
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.passwordEncoder(new BCryptPasswordEncoder())
.withUser("xuan").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1", "vip2")
.and()
.withUser("root").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1", "vip2", "vip3")
.and()
.withUser("guest").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1");
}
}
第三步:我们需要整合SpringSecurity和Thymeleaf来确保某个用户只能访问某些权限
注意的是这个功能在SpringBoot2.0.9及以前才有
- 要先引入SpringSecurity整和Thymeleaf依赖
<!--thymeleaf整和springsecurity-->
<!-- https://mvnrepository.com/artifact/org.thymeleaf.extras/thymeleaf-extras-springsecurity4 -->
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity4</artifactId>
<version>3.0.4.RELEASE</version>
</dependency>
- 其次需要引入命名空间
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4"
- 接着进行判断 判断是否已经登录
<!--未登录-->
<!--这一句话是判断用户是否已经登录 如果未登录 显示登录页面-->
<div sec:authorize="!isAuthenticated()">
<a class="item" th:href="@{/login}">
<i class="address card icon"></i> 登录
</a>
</div>
<!--已登录 用户名和注销-->
<!--如果已经登录 显示用户名和注销按钮-->
<div sec:authorize="isAuthenticated()">
<a class="ui-menu-item">
用户名:<span sec:authentication="name">name</span>
<!--获得当前用户角色-->
角色:<span sec:authentication="principal.authorities"></span>
</a>
<a class="item" th:href="@{/logout}">
<i class="sign-out icon"></i> 注销
</a>
</div>
- 然后需要给每一个level一个显示的条件,你具有这个权限才给你显示
sec:authorize="hasRole('vip1')"
- 最后需要关闭跨站请求伪造(CSRF)不然退出是会报错的
//关闭跨站访问(跨站请求伪造)防止跨站攻击
http.csrf().disable();
第四步:定制首页
- 首先需要在controller层指定自定义首页访问接口
@RequestMapping("/toLogin")
public String login(){
return "views/login";
}
- 其次在SecurityConfig配置访问的登录页 formLogin().loginPage("/toLogin")
//授权
@Override
protected void configure(HttpSecurity http) throws Exception {
//配置拦截权限
http
.authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/level1/**").hasRole("vip1")
.antMatchers("/level2/**").hasRole("vip2")
.antMatchers("/level3/**").hasRole("vip3")
.and().formLogin().loginPage("/toLogin")
.and().rememberMe()
.and().logout().logoutSuccessUrl("/");
//没有权限默认到登录页面 http.formLogin();
//注销 http.logout();
//记住我 http.rememberMe();
//关闭跨站访问(跨站请求伪造)
http.csrf().disable();
}
- 接着在登录页修改表单提交路径
<form th:action="@{/toLogin}" method="post">
最后测试------------------测试成功