【SpringBoot】SpringBoot基础教程(12)
前言:
本文内容:SpringSecurity环境搭建、用户认证和授权、注销及权限控制
推荐免费SpringBoot基础教程视频:【狂神说Java】SpringBoot最新教程通俗易懂_哔哩哔哩_bilibili
SpringSecurity环境搭建
在web开发中,安全第一位!过滤器,拦截器…
网站:安全应该在什么时候考虑? 设计之初
-
出现漏洞,造成隐私泄露
-
架构一旦确定,不方便修改
安全框架:shiro SpringSecurity
- 功能权限
- 访问权限
- 菜单权限
- …拦截器,过滤器,需要大量原生代码…
简介
Spring Security是针对Spring项目的安全框架,也是Spring Boot底层安全模块默认的技术选型,它可以实现强大的web安全控制,对于安全控制,仅需要引入spring-boot-starter-security
模块,进行少量的配置,即可实现强大的安全管理。
记住这几个类:
-
(该类在5.7版本已被弃用)WebSecurityConfigurerAdapter
:自定义Security策略 -
AuthenticationManagerBuilder
:自定义认证策略 -
@EnableWebSecurity
:开启WebSecurity模式
Spring Security的两个主要目标是认证和授权
认证(Authentication)
授权(Authorization)
这两个概念是通用的,并不局限于Spring Security。
环境搭建
-
新建项目
springboot-06-security
,并引入web启动器 -
在pom中引入
security
和thymleaf
1
2
3
4
5
6
7
8
9
10<!-- thymeleaf-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!-- security-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency> -
引入静态资源
百度云下载 提取码:bm3a
-
在
controller
包下新建RooterController
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44package com.jokerdig.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* @author Joker大雄
* @data 2022/7/23 - 11:47
**/
public class RooterController {
// 首页
public String index(){
return "index";
}
// 去登录
public String toLogin(){
return "views/login";
}
// level1
public String toLevel1(int id){
return "views/level1/"+id;
}
// level2
public String toLevel2(int id){
return "views/level2/"+id;
}
// level3
public String toLevel3(int id){
return "views/level3/"+id;
}
} -
运行测试
注意:
您可以在 http://localhost:8080/ 访问应用程序,这会将浏览器重定向到默认登录页面。您可以使用随机生成的密码提供 的默认用户名
user
,该密码将记录到IDEA
控制台中。然后,浏览器将转到最初请求的页面。当然:如果你使用旧版的写法就没有上面这一步了,可以直接访问首页!
要注销,您可以访问 http://localhost:8080/logout,然后确认您希望注销。
-
准备工作完成
用户认证和授权
认证(Authentication)
授权(Authorization)
-
在
config
包下新建SecurityConfig
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47package com.jokerdig.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;
/**
* @author Joker大雄
* @data 2022/7/23 - 12:14
**/
// 启动Security
public class SecurityConfig extends WebSecurityConfigurerAdapter {
// 这里仅用旧版做演示
protected void configure(HttpSecurity http) throws Exception {
http
.formLogin()
// 账号和密码接收
.usernameParameter("username")
.passwordParameter("password")
// 不同的功能之间用and()连接
.and()
// 权限
.authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/level1/**").hasRole("vip1")
.antMatchers("/level2/**").hasRole("vip2")
.antMatchers("/level3/**").hasRole("vip3");
}
// 这个方法在在新版本失效了,这里仅作演示
// 密码编码 passwordEncoder
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.passwordEncoder(new BCryptPasswordEncoder())
.withUser("admin").password(new BCryptPasswordEncoder().encode("123456")).roles("vip2","vip3")
.and()
.withUser("root").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1","vip2","vip3")
.and()
.withUser("guest").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1");;
}
}这是新版的写法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40/**
* @author Joker大雄
* @data 2022/7/23 - 12:14
**/
// 启动Security
public class SecurityConfig{
/**
旧版本的两个方法
configure(HttpSecurity)
configure(WebSecurity)
新版本的代替
@Bean
SecurityFilterChain
@Bean
WebSecurityCustomizer
**/
// 链式编程 设置访问权限 通过 SecurityFilterChain 这是新的写法
// 新的写法不需要 extends WebSecurityConfigurerAdapter
// 因为新版没有 configure(AuthenticationManagerBuilder auth),所以使用旧版演示
SecurityFilterChain filterChain(HttpSecurity http) throws Exception{
http
// 新版formLogin的写法
.formLogin()
// 账号和密码接收
.usernameParameter("username")
.passwordParameter("password")
// 不同的功能之间用and()连接
.and()
// 权限
.authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/level1/**").hasRole("vip1")
.antMatchers("/level2/**").hasRole("vip2")
.antMatchers("/level3/**").hasRole("vip3");
return http.build();
}
} -
运行测试
如果使用新版不能跳转,原因是需要配置
AuthenticationSuccessHandler
也可以降级到
2.5.3
版本便能正常访问发现没有权限只能访问到首页,访问其他页面直接跳转到登录页,登录有权限的账号可以访问对应权限的页面
-
认证授权完成
注销及权限控制
注销
-
在
Security
中编写注销功能及配置1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49package com.jokerdig.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;
/**
* @author Joker大雄
* @data 2022/7/23 - 12:14
**/
// 启动Security
public class SecurityConfig extends WebSecurityConfigurerAdapter {
// 这里仅用旧版做演示
protected void configure(HttpSecurity http) throws Exception {
http
.formLogin()
// 账号和密码接收
.usernameParameter("username")
.passwordParameter("password")
// 不同的功能之间用and()连接
.and()
// 权限
.authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/level1/**").hasRole("vip1")
.antMatchers("/level2/**").hasRole("vip2")
.antMatchers("/level3/**").hasRole("vip3");
// 注销功能 注销成功跳转到首页
http.logout().logoutSuccessUrl("/");
}
// 这个方法在在新版本失效了,这里仅作演示
// 密码编码 passwordEncoder
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication()
.passwordEncoder(new BCryptPasswordEncoder())
.withUser("admin").password(new BCryptPasswordEncoder().encode("123456")).roles("vip2","vip3")
.and()
.withUser("root").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1","vip2","vip3")
.and()
.withUser("guest").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1");;
}
} -
为
index.html
页面添加注销按钮1
2
3
4<!-- 注销-->
<a class="item" th:href="@{/logout}">
<i class="sign-out icon"></i> 注销
</a> -
运行测试
点击注销,注销后跳转到首页
-
注销功能完成
权限控制
我们希望用户看不到自己权限之外的内容
-
在pom引入security-thymeleaf
1
2
3
4
5
6<!-- security-thymeleaf整合包-->
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity4</artifactId>
<version>3.0.4.RELEASE</version>
</dependency> -
修改index.html页面
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
<!--导入security整合thymeleaf的命名空间-->
<html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<title>首页</title>
<!--semantic-ui-->
<link href="https://cdn.bootcss.com/semantic-ui/2.4.1/semantic.min.css" rel="stylesheet">
<link th:href="@{/css/style.css}" rel="stylesheet">
</head>
<body>
<!--主容器-->
<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="@{/login}">
<i class="address card icon"></i> 登录
</a>
</div>
<!--已经登录-->
<div sec:authorize="isAuthenticated()">
<!-- 显示用户名-->
<a class="item">
用户名:<span sec:authentication="name"></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>
<script th:src="@{/js/jquery-3.1.1.min.js}"></script>
<script th:src="@{/js/semantic.min.js}"></script>
</body>
</html> -
运行测试
根据不同权限显示不同内容,包括用户名显示
-
权限控制功能完成