前言
笔记整理来源于狂神视频https://www.bilibili.com/video/BV1PE411i7CV
主要包含SpringSecurity简介以及快速上手
一、为什么使用SpringSecurity
在web开发中,安全占据第一位置
我们可以通过一些简单的安全策略,例如过滤器,拦截器保证安全
安全是一个非功能性需求,做网站,后台应该在设计之初进行考虑,在我们设计之前
就应该把这些东西考虑进去,虽然我们可以通过拦截器,过滤器来完成需求,但是会有大量的原生代码,冗余
,而通过SpringSecurity,我们只需要进行简单的调用,便可实现无数复杂的功能
项目中所涉及的安全性问题
- 功能权限
- 访问权限
- 菜单权限
市面上比较知名的安全的框架
- shiro
- SpringSecurity
这两个框架除了类不一样,名字不一样,其他地方有很多相似之处,我们此次便去了解SpringSecurity
一、关于SpringSecurity
参考文档:
https://docs.spring.io/spring-security/site/docs/5.2.0.RELEASE/reference/htmlsingle
Spring Security is a powerful and highly customizable authentication and access-control framework. It is the de-facto standard for securing Spring-based applications.
Spring Security is a framework that focuses on providing both authentication and authorization to Java applications. Like all Spring projects, the real power of Spring Security is found in how easily it can be extended to meet custom requirements
Spring security 是一个功能强大且高度可定制的身份验证和访问控制框架。它是保护基于 spring 的应用程序的事实上的标准。spring security 是一个框架,专注于为 java应用程序提供身份验证和授权。就像所有的spring项目一样,spring安全性的真正威力在于它可以很容易地扩展以满足客户需求
Spring Security是针对Spring项目的安全框架,也是Spring Boot底层安全模板默认的技术选型,他可以实现强大的Web安全控制,对于安全控制,我们仅需要引入spring-boot-starter-security模块,进行少量的配置,即可实现强大的安全管理
需要牢记的几个类
WebSecurityConfigurerAdapter: 自定义Security策略
AuthenticationManagerBuilder: 自定义认证策略
@EnableWebSecurity: 开启WebSecurity模式
“认证”(Authentication)
“授权”(Authorization)
这些概念是通用的,而不是只在Spring Security中存在
导入模板素材
首页index.html:
RouterControler跳往各个页面
package com.kuang.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class RouterControler {
@RequestMapping({"/","/index"})
public String index(){
return "index";
}
@RequestMapping("/toLogin")
public String toLogin(){
return "views/login";
}
//跳往等级页面
@RequestMapping("/level1/{id}")
public String toLevel1(@PathVariable("id") int id){
return "views/level1/"+id;
}
//跳往等级页面
@RequestMapping("/level2/{id}")
public String toLevel2(@PathVariable("id") int id){
return "views/level2/"+id;
}
//跳往等级页面
@RequestMapping("/level3/{id}")
public String toLevel3(@PathVariable("id") int id){
return "views/level3/"+id;
}
}
SecurityConfig配置SpringSecurity:
package com.kuang.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;
//AOP思想
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
//链式编程
//http安全策略
//授权
@Override
protected void configure(HttpSecurity http) throws Exception {
//首页所有人可访问,功能页只有对应有权限的人才能访问
//请求授权的规则
http.authorizeRequests().antMatchers("/").permitAll()
.antMatchers("/level1/**").hasRole("vip1")
.antMatchers("/level2/**").hasRole("vip2")
.antMatchers("/level3/**").hasRole("vip3");
//没有权限默认跳转至登录页
http.formLogin().loginPage("/toLogin").usernameParameter("user").passwordParameter("pwd").loginProcessingUrl("/login").defaultSuccessUrl("/index");
//开启注销功能,跳转回登录页
http.logout().logoutSuccessUrl("/toLogin");
http.csrf().disable();
//开启记住密码功能
http.rememberMe().rememberMeParameter("remember");
}
//认证,springboot 2.1.X可以直接使用
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//这些数据正常从数据库中获得
//下面是从内存中取得
auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
.withUser("zs").password(new BCryptPasswordEncoder().encode("123")).roles("vip1","vip2","vip3")
.and()
.withUser("ls").password(new BCryptPasswordEncoder().encode("123")).roles("vip1","vip2")
.and()
.withUser("guest").password(new BCryptPasswordEncoder().encode("123")).roles("vip1")
;
}
}
配置完成进入网页,点击vip2自动跳转至登录页
此时输入数据进行登录,发现报错
这是因为我们允许通过的密码没有经过加密,不安全,被Spring Security驳回
Spring Security5.0+ 新增了很多加密办法
这里我们采用BCryptPasswordEncoder方式的加密
//认证,springboot 2.1.X可以直接使用
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//这些数据正常从数据库中获得
//下面是从内存中取得
auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
.withUser("zs").password(new BCryptPasswordEncoder().encode("123")).roles("vip1","vip2","vip3")
.and()
.withUser("ls").password(new BCryptPasswordEncoder().encode("123")).roles("vip1","vip2")
.and()
.withUser("guest").password(new BCryptPasswordEncoder().encode("123")).roles("vip1")
;
}
进行登录跳转:
跳转成功
那么接下来,如果我们以游客guest身份登录,登录之后只想让他看到页面的部分功能应该怎么办呢?这个时候需要通过Thymeleaf加SpringSecurity来实现
导入入Thymeleaf加SpringSecurity整合包:
<!-- https://mvnrepository.com/artifact/org.thymeleaf.extras/thymeleaf-extras-springsecurity5 -->
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity5</artifactId>
<version>3.0.4.RELEASE</version>
</dependency>
在页面引入命名空间:
xmlns:th="http://www.thymeleaf.org" xmlns:sec=http://www.thymeleaf.org/extras/spring-security
在html利用sec:authorize="hasRole(‘xxx’)"对权限进行筛选
index页面代码:
<!--主容器-->
<div class="ui container">
<div class="ui segment" id="index-header-nav" th:fragment="nav-menu">
<div class="ui secondary menu">
<a class="item" th:href="@{/index}">首页</a>
<!--登录注销-->
<div class="right menu">
<!--未登录显示登录按钮-->
<div sec:authorize="!isAuthenticated()">
<a class="item" th:href="@{/toLogin}">
<i class="address card icon"></i> 登录
</a>
</div>
<!--如果已登录,显示用户名,注销-->
<div sec:authorize="isAuthenticated()">
<a class="item">
用户名:<span sec:authentication="name"></span>
vip等级:<span sec:authentication="principal.authorities"></span>
</a>
</div>
<div sec:authorize="isAuthenticated()">
<a class="item" th:href="@{/logout}">
<i class="sign-out icon"></i> 注销
</a>
</div>
</div>
</div>
</div>
<div class="ui segment" style="text-align: center">
<h3>Spring Security</h3>
</div>
<div>
<br>
<div class="ui three column stackable grid">
<div class="column" sec:authorize="hasRole('vip1')">
<div class="ui raised segment">
<div class="ui">
<div class="content">
<h5 class="content">Level 1</h5>
<hr>
<div><a th:href="@{/level1/1}"><i class="bullhorn icon"></i> Level-1-1</a></div>
<div><a th:href="@{/level1/2}"><i class="bullhorn icon"></i> Level-1-2</a></div>
<div><a th:href="@{/level1/3}"><i class="bullhorn icon"></i> Level-1-3</a></div>
</div>
</div>
</div>
</div>
<div class="column" sec:authorize="hasRole('vip2')">
<div class="ui raised segment">
<div class="ui">
<div class="content">
<h5 class="content">Level 2</h5>
<hr>
<div><a th:href="@{/level2/1}"><i class="bullhorn icon"></i> Level-2-1</a></div>
<div><a th:href="@{/level2/2}"><i class="bullhorn icon"></i> Level-2-2</a></div>
<div><a th:href="@{/level2/3}"><i class="bullhorn icon"></i> Level-2-3</a></div>
</div>
</div>
</div>
</div>
<div class="column" sec:authorize="hasRole('vip3')">
<div class="ui raised segment">
<div class="ui">
<div class="content" >
<h5 class="content">Level 3</h5>
<hr>
<div><a th:href="@{/level3/1}"><i class="bullhorn icon"></i> Level-3-1</a></div>
<div><a th:href="@{/level3/2}"><i class="bullhorn icon"></i> Level-3-2</a></div>
<div><a th:href="@{/level3/3}"><i class="bullhorn icon"></i> Level-3-3</a></div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
我们为zs赋予的权限为vip3,zs进行登录所看到的画面
我们为ls赋予的权限为vip2,ls进行登录所看到的画面:
开启记住密码功能:
http.rememberMe();
点击Remember me on this computer,用zs账户登录之后关闭浏览器,再次打开发现
账户信息依旧存在
我们查看内存中的cookie:
我们发现security自动创建了remember-me的cookie对象,且有效期为14天
定制首页
**在源码中我们发现:
* /**@Override
* protected void configure(HttpSecurity http) throws Exception {
*
* http.authorizeRequests().antMatchers("").hasRole("USER").and().formLogin()
* .usernameParameter("username") // default is username
* .passwordParameter("password") // default is password
* .loginPage("/authentication/login") // default is /login with an HTTP get
* .failureUrl("/authentication/login?failed") // default is /login?error
* .loginProcessingUrl("/authentication/login/process"); // default is /login
* // with an HTTP
* // post
* }**
我们可以通过loginPage可以设置登录页,通过failureUrl设置失败的登录页
由此我们可以设置自己的登录页
http.formLogin().loginPage("/toLogin").usernameParameter("user").passwordParameter("pwd").loginProcessingUrl("/login").defaultSuccessUrl("/index");
这里loginProcessingUrl()相当于一个中转站,前台界面提交表单之后跳转到这个路径进行User DetailsService的验证,如果成功, defaultSuccessUrl()如果失败,那么转向failureUrl("/error.html")
**最终样式:**