一、创建测试工程
1、引入依赖:这时还没有引入spring-security
org.springframework.boot
spring-boot-starter-thymeleaf
org.springframework.boot
spring-boot-starter-web
2、编写前端页面:

level1/1.html:
Insert title here
返回
罗汉拳
罗汉拳站当央,打起来不要慌
level1/2.html:
Insert title here
返回
武当长拳
长一点在长一点
level1/3.html:
Insert title here
返回
全真剑法
全都是真的
level2/1.html:
Insert title here
返回
太极拳
一个西瓜圆又圆 劈它一刀成两半 你一半来 给你你不要 给他他不收 那就不给 把两人撵走 他们不走你走 走啦,一挥手,伤自尊
不买西瓜别缠我,缓慢纠缠様 两人缠我赖皮,手慢动作左右挥动 看我厉害,转头缓步拍苍蝇状 拍死了,手抱西瓜状+奥特曼十字手+广播操准备运动的站立
level2/2.html:
Insert title here
返回
七伤拳
练这拳的人全都死了
level2/3.html:
Insert title here
返回
梯云纵
踩自己的脚往上跳
level3/1.html:
Insert title here
返回
葵花宝典
欲练神功,挥刀自宫
level3/2.html:
Insert title here
返回
龟派气功
龟-派-气-功-波
level3/3.html:
Insert title here
返回
独孤九剑
欲练此剑,必先犯贱
login.html:
Insert title here
欢迎登陆武林秘籍管理系统
welcome.html:
Insert title here
欢迎光临武林秘籍管理系统
游客您好,如果想查看武林秘籍 请登录
普通武功秘籍
高级武功秘籍
绝世武功秘籍
3、编写访问页面的controller:此时访问/时会路由到welcome.html
@Controller
public class KungfuController {
private final String PREFIX = "pages/";
/**
* 欢迎页
*
* @return
*/
@GetMapping("/")
public String index() {
return "welcome";
}
/**
* 登陆页
*
* @return
*/
@GetMapping("/userlogin")
public String loginPage() {
return PREFIX + "login";
}
/**
* level1页面映射
*
* @param path
* @return
*/
@GetMapping("/level1/{path}")
public String level1(@PathVariable("path") String path) {
return PREFIX + "level1/" + path;
}
/**
* level2页面映射
*
* @param path
* @return
*/
@GetMapping("/level2/{path}")
public String level2(@PathVariable("path") String path) {
return PREFIX + "level2/" + path;
}
/**
* level3页面映射
*
* @param path
* @return
*/
@GetMapping("/level3/{path}")
public String level3(@PathVariable("path") String path) {
return PREFIX + "level3/" + path;
}
}
4、启动测试:http://localhost:8080/

若SpringBoot版本太低,在访问时可能会因为引入的thymeleaf版本太低(不支持html的一些新标签)而报错,此时可在pom中更改SpringBoot默认引用的thymeleaf的版本:在properties节点中更改
二、认证和授权
安全框架的两个主要职能是“认证”和“授权”(或者访问控制),这两个主要职能也是Spring Security 的两个目标。
认证(Authentication):辨明主体是谁,是建立一个安全主体的过程(主体一般是指用户、设备或一些可以在你的应用程序中执行动作的其他系统)
授权(Authorization):主体在系统中能干什么,确定一个主体是否允许在你的应用程序执行一个动作(比如查看某个页面资源)的过程。授权之前主体的身份已经有认证,即授权是在认证之后决定主体拥有哪些权限
三、使用Spring Security
1、引入spring security
org.springframework.boot
spring-boot-starter-security
2、参照官网编写一个spring security的配置类:示例地址
示例:配置类继承于WebSecurityConfigurerAdapter,并须添加@EnableWebSecurity注解
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/css/**", "/index").permitAll()
.antMatchers("/user/**").hasRole("USER")
.and()
.formLogin()
.loginPage("/login").failureUrl("/login-error");
}
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.withUser("user").password("password").roles("USER");
}
}
自定义SecurityConfig :定制授权规则
@EnableWebSecurity
public class MySecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
//定制授权规则
http.authorizeRequests().antMatchers("/").permitAll()//所有用户可访问"/",即主页
.antMatchers("/level1/**").hasRole("VIP1")//访问URL中含有/level1的资源需有VIP1权限
.antMatchers("/level2/**").hasRole("VIP2")//访问URL中含有/level2的资源需有VIP2权限
.antMatchers("/level3/**").hasRole("VIP3");//访问URL中含有/level3的资源需有VIP3权限
}
}
此时再访问未授权的资源时会被拒绝

开启登录功能:调用HttpSecurity的formLogin()
@EnableWebSecurity
public class MySecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/").permitAll()//所有用户可访问"/",即主页
.antMatchers("/level1/**").hasRole("VIP1")//访问URL中含有/level1的资源需有VIP1权限
.antMatchers("/level2/**").hasRole("VIP2")//访问URL中含有/level2的资源需有VIP2权限
.antMatchers("/level3/**").hasRole("VIP3");//访问URL中含有/level3的资源需有VIP3权限
//开启自动配置的登录功能
http.formLogin();
}
}
在访问未经授权的资源时会发送/login请求跳转到Security提供的登录页面,如果填写的登录信息有误会发送(login?error)请求跳转到Security提供的登录错误页面,我们也可以设置登录的用户名密码等字段名的信息
登录页:

登录失败:

3、认证:以上为Security的授权,接下来是Security的认证:
@EnableWebSecurity
public class MySecurityConfig extends WebSecurityConfigurerAdapter {
/**
* 授权
*
* @param http
* @throws Exception
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/").permitAll()//所有用户可访问"/",即主页
.antMatchers("/level1/**").hasRole("VIP1")//访问URL中含有/level1的资源需有VIP1权限
.antMatchers("/level2/**").hasRole("VIP2")//访问URL中含有/level2的资源需有VIP2权限
.antMatchers("/level3/**").hasRole("VIP3");//访问URL中含有/level3的资源需有VIP3权限
//开启自动配置的登录功能
http.formLogin();
}
/**
* 开启认证功能
*
* @param auth
* @throws Exception
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()//在内存的数据中认证
.withUser("zhangsan").password("123456").roles("VIP1", "VIP2")//配置用户名、密码和角色信息
.and()
.withUser("lisi").password("654321").roles("VIP3");
}
}
在高版本的Spring Security中使用以上的认证方式可能会报错:
There is no PasswordEncoder mapped for the id "null"
这是因为高版本中需要指定密码的加密方式,代码如下:
/**
* 开启认证功能
*
* @param auth
* @throws Exception
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()//在内存的数据中认证
.passwordEncoder(new BCryptPasswordEncoder())//指定密码的编码方式
.withUser("zhangsan").password(new BCryptPasswordEncoder().encode("123456")).roles("VIP1", "VIP2")//配置用户名、密码和角色信息
.and()
.withUser("lisi").password(new BCryptPasswordEncoder().encode("654321")).roles("VIP3");//使用Bcrypt对密码进行加密
}
4、注销:调用HttpSecurity.logout()开启注销功能,前端访问/logout请求即可
@EnableWebSecurity
public class MySecurityConfig extends WebSecurityConfigurerAdapter {
/**
* 授权
*
* @param http
* @throws Exception
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/").permitAll()//所有用户可访问"/",即主页
.antMatchers("/level1/**").hasRole("VIP1")//访问URL中含有/level1的资源需有VIP1权限
.antMatchers("/level2/**").hasRole("VIP2")//访问URL中含有/level2的资源需有VIP2权限
.antMatchers("/level3/**").hasRole("VIP3");//访问URL中含有/level3的资源需有VIP3权限
//开启自动配置的登录功能
http.formLogin();
//开启自动配置的注销功能
http.logout();
}
}
前端请求:请求地址/logout,请求方式必须是post
开启自动配置的注销功能后只需要访问/logout请求即可清空session(Spring Security已为我们提供了/logout请求的处理,无需我们编写处理逻辑,应该也可以自定义处理逻辑);注销成功后默认会重定向到登录页(访问/login?logout),我们可以定制重定向的路径:设置logoutSuccessUrl的值即可
http.logout().logoutSuccessUrl("/");
四、页面根据授权展示内容
1、引入Spring Security整合thymeleaf的依赖:注意版本要和Spring Security对应
org.thymeleaf.extras
thymeleaf-extras-springsecurity5
3.0.4.RELEASE
由于SpringBoot默认指定了Spring Security和thymeleaf以及他们整合时的版本
5.1.3.RELEASE
3.0.11.RELEASE
3.0.4.RELEASE
但是该版本可能和thymeleaf-extras-springsecurity5不匹配,这时我们可以在pom文件中修改版本:若匹配则无需修改
2、在页面上引入Security的名称空间
在页面上使用引入的名称空间:
①未登录则显示登录按钮,否则不显示:sec:authorize="!isAuthenticated()"
②登录成功显示注销按钮,并显示当前登录者和授予权限:sec:authentication="name"获取登录者,sec:authentication="principal.authorities"获取授予权限
③根据登录者拥有的角色显示对应的武功秘籍:sec:authorize=“hasRole(‘VIP3’)”
五、记住我
1、开启记住我功能:调用HttpSecurity的rememberMe()
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/").permitAll()//所有用户可访问"/",即主页
.antMatchers("/level1/**").hasRole("VIP1")//访问URL中含有/level1的资源需有VIP1权限
.antMatchers("/level2/**").hasRole("VIP2")//访问URL中含有/level2的资源需有VIP2权限
.antMatchers("/level3/**").hasRole("VIP3");//访问URL中含有/level3的资源需有VIP3权限
//开启自动配置的登录功能
http.formLogin();
//开启自动配置的注销功能
http.logout().logoutSuccessUrl("/");
//开启记住我功能
http.rememberMe();
}

原理:开启Spring Security的记住我功能之后,一旦我们勾选了记住我,Spring Security就会给浏览器发送一个默认名为remember-me的cookie,默认的有效期限为14天,这样我们14天内(14天后该cookie会自动删除)使用该浏览器再次访问该网址的时候浏览器就会携带着这个cookie,Spring Security就会根据该cookie查找cookie的有效性以及登录人员的信息等,我们点击注销后,Spring Security会删除浏览器端的cookie

注销:
