Spring Security入門基礎
文章目錄
- Spring Security入門基礎
- 一,Spring Security的使用
- 1.1 基本術語
- 1.2 基本使用
- 1.2.1 引入依賴
- 1.2.2 組態檔
- 1.2.3 測驗介面
- 1.3 過濾器鏈的15個過濾器
- 1.4 自定義用戶認證 WebSecurityConfigurerAdapter
- 1.4.1 configure(HttpSecurity http)
- 1.4.2 configure(AuthenticationManagerBuilder auth)
- 1.4.3 configure(WebSecurity web)
- 1.4.4 自定義表單登錄
一,Spring Security的使用
1.1 基本術語
SpringSecurity是一個強大的可高度定制的認證和授權框架,對于Spring應用來說它是一套Web安全標準,Spring Security注重于為java應用提供認證和授權功能,
OAuth2是用于授權的行業標準協議,為簡化客戶端開發提供類特定的授權流,
Resource owner(資源擁有者):擁有該資源的最終用戶,有訪問資源的賬號密碼,
Resource server(資源服務器):擁有受保護資源的服務器,如果請求包含正確的訪問令牌,可以訪問資源,
Client(客戶端):訪問資源的客戶端,會使用訪問令牌去獲取資源服務器的資源,可以是瀏覽器,移動設備或者服務器,
Authorization server(授權服務器):用于授權用戶的服務器,如果客戶端授權通過,發放訪問資源服務器的令牌,
四種授權模式:
-
Authorization Code(授權碼模式):客戶端先將用戶導向授權服務器,登陸后獲取授權碼,然后進行授權,最后根據授權碼獲取訪問令牌,
-
Implicit(簡化模式):和授權碼模式相比,取消了獲取授權碼的程序,直接獲取訪問令牌,
-
Resource Owner Password Credential(密碼模式):客戶端直接向用戶獲取用戶名和密碼,之后向授權服務器獲取訪問令牌,
-
Client Credential(客戶端模式):客戶端直接通過客戶端授權,從授權服務器獲取訪問令牌,
1.2 基本使用
1.2.1 引入依賴
新創建模塊tools-security,引入依賴:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 安全認證 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
1.2.2 組態檔
server:
port: 9040
spring:
application:
name: tools-security
1.2.3 測驗介面
@RequestMapping("test")
public String test() {
return "test";
}
運行專案,在以下截取的部分日志中,找到 Using generated security password,后面的字串為原始密碼(每次運行結果不同),賬號為 user
2021-10-23 15:52:58.472 WARN [tools-security,,] 100588 --- [ restartedMain] o.s.c.s.a.z.ZipkinAutoConfiguration : Check result of the [RestTemplateSender{http://localhost:9411/api/v2/spans}] contains an error [CheckResult{ok=false, error=org.springframework.web.client.HttpClientErrorException$BadRequest: 400 Bad Request: [Empty JSON_V2 message]}]
2021-10-23 15:52:59.049 INFO [tools-security,,] 100588 --- [ restartedMain] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor'
2021-10-23 15:52:59.303 INFO [tools-security,,] 100588 --- [ restartedMain] .s.s.UserDetailsServiceAutoConfiguration :
Using generated security password: 0c731ade-806a-4a8e-83e1-e11044fe0f48
訪問 http://localhost:9040/test , 會先跳到默認的登錄頁面,輸入賬號(user)密碼(0c731ade-806a-4a8e-83e1-e11044fe0f48)即可進行訪問到 /test 介面,
1.3 過濾器鏈的15個過濾器
1. org.springframework.security.web.context.SecurityContextPersistenceFilter
首當其沖的一個過濾器
SecurityContextPersistenceFilter主要是使用SecurityContextRepository在session中保存或更新一個securityContext,并將SecurityContext給以后的過濾器使用,來為后續Filter建立所需的背景關系,
2. org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter
此過濾器用于集成SecurityContext到Spring異步執行機制中的WebAsyncManager
3. org.springframework.security.web.header.HeaderWriterFilter
向請求的Header中添加相應的資訊,可在http標簽內部使用security:headers來控制
4. org.springframework.security.web.csrf.CsrfFilter
csrf又稱跨域請求偽造,SpringSecurity會對所有post請求驗證是否包含系統生成的CSRF的token資訊,如果不包含,則報錯,起到防止CSRF攻擊的效果,
5. org.springframework.security.web.authentication.logout.LogoutFilter
匹配URL為/logout的請求,實作用戶退出,清除認證資訊,
6. org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter
認證操作全靠這個過濾器,默認匹配URL為/login且必須為post請求,
7. org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter
如果沒有在組態檔中指定認證頁面,則由該過濾器生成一個默認認證頁面
8. org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter
由此過濾器可以生產一個默認的退出登錄頁面
9. org.springframework.security.web.authentication.www.BasicAuthenticationFilter
此過濾器會自動決議http請求中頭部名字為Authentication,且以Basic 開頭的頭資訊,
10. org.springframework.security.web.savedrequest.RequestCacheAwareFilter
通過HttpSessionRequestCache內部維護了一個RequestCache,用于快取HttpServletRequest
11. org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter
針對ServletRequest進行了一次包裝,使得request具有更加豐富的API
12. org.springframework.security.web.authentication.AnpnymousAuthenticationFilter
當SecurityContextHolder中認證資訊為空,則會創建一個匿名用戶存入到SecurityContextHolder中,
spring security為了兼容未登錄的訪問,也走了一套認證流程,只不過是一個匿名的身份,
13. org.springframework.security.web.session.SessionManagementFilter
SecurityContextRepository限制同一用戶開啟多個回話的數量
14. org.springframework.security.web.access.ExceptionTranslationFilter
例外轉移過濾器位于整個SpringSecurityFilterChain的后方,用來轉換整個鏈路中出現的例外,
15. org.springframework.security.web.access.intercept.FilterSecurityinterceptor
獲取所配置資源訪問的授權資訊,根據SecurityContextHolder中存盤的用戶資訊來決定其是否有權限
1.4 自定義用戶認證 WebSecurityConfigurerAdapter
前面的做法是使用默認賬號user和應用啟動時自動生成的密碼進行訪問認證,但是,每個系統都應該有自己的用戶體系,需要自定義用戶的登錄認證和介面的認證控制,這里就需要對WebSecurityConfiguration進行配置,主要關注三個方法:configure(HttpSecurity http),configure(AuthenticationManagerBuilder auth)和configure(WebSecurity),
- HttpSecurity 允許基于選擇匹配在資源級配置基于網路的安全性,并宣告任何其他網址需要成功驗證,也就是對角色的權限——所能訪問的路徑做出限制,
- AuthenticationManagerBuilder 用于通過允許AuthenticationProvider容易地添加來建立認證機制,配置的是認證資訊 主要配置用戶身份和角色資訊也就是說用來記錄賬號,密碼,角色資訊,
- WebSecurity 用于影響全域安全性(配置資源,設定除錯模式,通過實作自定義防火墻定義拒絕請求)的配置設定,一般用于配置全域的某些通用事物,例如靜態資源等,
創建WebSecurityConfiguration.java,繼承WebSecurityConfigurerAdapter
@Configuration
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
}
1.4.1 configure(HttpSecurity http)
HttpSecurity 有重要的兩個方法authorizeRequests()和requestMatchers()
authorizeRequests()
授權管理控制的方法
這個方法回傳一個ExpressionUrlAuthorizationConfigurer.ExpressionInterceptUrlRegistry物件,
authorizeRequests定義那些url需要被保護,那些不需要進行保護,通常出來在配置的第一行,
Security所有的權限控制都基于這個類進行控制,如:
http.authorizeRequests().anyRequest().authenticated()要求所有介面都需要進行權限認證,等同于http.authorizeRequests().antMatchers("/**").authenticated();
而http.authorizeRequests().antMatchers("/**").permitAll()這個配置則要求所有介面都不需要進行權限認證,等同于http.authorizeRequests().anyRequest().permitAll()
另外這兩個代碼中antMatchers方法則是配置匹配規則,即哪些介面需要進行權限認證或不需要進行權限認證,
anyRequest():所有介面
authenticated():需要進行權限認證
permitAll():不需要進行權限認證
antMatchers(String):匹配介面
requestMatchers()
取得RequestMatcherConfigurer物件并配置允許過濾的路由;如requestMatchers().anyRequest()等同于http.authorizeRequests().anyRequest().access(“permitAll”);
舉例:
package com.lmc.security.conf;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
/**
* @author lmc
* @Description: TODO
* @Create 2021-10-23 19:55
* @version: 1.0
*/
@Configuration
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/403", "/404", "/500").permitAll() // 訪問403/404/500時不需要認證
.anyRequest().authenticated() // 其他請求需要認證
.and()
.formLogin() // 通過form進行登錄
.and()
.csrf().disable(); // 關閉跨站請求偽造保護
}
}
在以上配置中,我定義了請求為/403,/404和/500的三個介面不需要接受認證,直接放行;然后其他請求都需要認證;然后登錄時要使用form方式,同時關閉跨站請求偽造保護,
在配置之前,我已經創建了403/404/500三個頁面
@Controller
public class PageController {
@RequestMapping("403")
public String accessError() {
return "403";
}
@RequestMapping("404")
public String notFound() {
return "404";
}
@RequestMapping("500")
public String serverError() {
return "500";
}
}
此時,運行專案,訪問 /404 介面能正常訪問,然后訪問 /test 介面時,會跳到Spring Security默認登錄頁面,輸入賬號密碼后才放行,
1.4.2 configure(AuthenticationManagerBuilder auth)
使用自定義用戶密碼登錄(記憶體方式)
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication() // 在記憶體存盤用戶資訊
.withUser("lmc").password("{noop}123") // 用戶名為lmc,密碼為123,{noop}表示密碼不加密
.roles("USER"); // 給用戶lmc賦予USER角色
}
spring security默認使用密碼是需要加密的,即.password(String)里的密碼已經是加密過的字串,如果在前面加上{noop},就表示非加密的密碼
.withUser("lmc").password("{noop}123")
也可以寫成
.passwordEncoder(new BCryptPasswordEncoder()).withUser("lmc").password(new BCryptPasswordEncoder().encode("123"))
注意,在security5.0之前默認加密方式是BC,所以只需要寫成
.withUser("lmc").password(new BCryptPasswordEncoder().encode("123"))
就可以,但是5.0后加密方式要自己定義,所以需要使用 passwordEncoder() 方法來定義加密方式,否則會報出以下例外:
There is no PasswordEncoder mapped for the id "null"
1.4.3 configure(WebSecurity web)
配置靜態資源,忽略認證
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring()
.antMatchers("/assets/**")
.antMatchers("/403", "/404", "/500")
.antMatchers("/login.html");
}
其實,configure(WebSecurity web) 的內容基本都可以在 configure(HttpSecurity http) 中配置,例如以上的配置在configure(HttpSecurity http)中配置如下:
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/403", "/404", "/500").permitAll() // 訪問403/404/500時不需要認證
.antMatchers("/assets/**").permitAll()
.antMatchers("/login.html").permitAll();
}
不過,我們一般將類似登錄頁面,以及錯誤頁面,靜態資源等通用事物,到 configure(WebSecurity web) 中配置,configure(HttpSecurity http) 偏向基于網路的安全性,也就是對角色權限的控制,涇渭分明,塑造更好的可觀性,
1.4.4 自定義表單登錄
我們經常不會使用security的默認登錄頁面,需要自己創建開發自定義登錄頁,可以通過HttpSecurity的方法進行操作,
先自定義登錄頁面:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Login Page</title>
<!-- 引入樣式 -->
<link rel="stylesheet" href="/assets/element-ui/index.css">
</head>
<style>
.el-header {
background-color: #1B73E8;
color: white;
text-align: left;
line-height: 60px;
}
.el-footer {
background-color: #F5F5F5;
color: black;
text-align: center;
line-height: 60px;
font-size: 12px;
}
.el-main {
background-color: white;
color: #333;
text-align: center;
line-height: 160px;
}
body > .el-container {
margin-bottom: 40px;
}
.el-row {
margin-bottom: 20px;
}
.el-col {
border-radius: 4px;
}
.bg-purple-dark {
background: #99a9bf;
}
.bg-purple {
background: #d3dce6;
}
.bg-purple-light {
/*background: #e5e9f2;*/
}
.grid-content {
border-radius: 4px;
min-height: 36px;
height: 800px;
}
.row-bg {
padding: 10px 0;
background-color: #f9fafc;
}
.el-card {
padding: 80px;
height: 638px;
}
</style>
<body>
<div id="app">
<el-container>
<el-header>lmc-tools</el-header>
<el-main>
<el-row :gutter="20">
<el-col :span="7"><div class="grid-content bg-purple"></div></el-col>
<el-col :span="10">
<div class="grid-content bg-purple-light">
<el-card class="box-card">
<el-form ref="form" action="/login" method="post" :model="form" :rules="rules" label-width="100px" name="loginform" class="login-form">
<el-form-item label="用戶名" prop="username">
<el-input v-model="form.username" name="username" clearable></el-input>
</el-form-item>
<el-form-item label="密碼" prop="password">
<el-input v-model="form.password" name="password" show-password type="password" clearable></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="submitForm('form')">登錄</el-button>
</el-form-item>
</el-form>
</el-card>
</div>
</el-col>
<el-col :span="7"><div class="grid-content bg-purple"></div></el-col>
</el-row>
</el-main>
<el-footer>Copyright ? 2021 lmc</el-footer>
</el-container>
</div>
</body>
<!-- 引入vue -->
<script src="/assets/vue/vue.js"></script>
<!-- 引入Element-UI組件庫 -->
<script src="/assets/element-ui/index.js"></script>
<script>
new Vue({
el: "#app",
data: {
form: {
username: '',
password: ''
},
rules: {
username: [
{required: true, message: '用戶名不能為空', trigger: 'blur'}
],
password: [
{required: true, message: '密碼不能為空', trigger: 'blur'}
]
}
},
methods: {
// 登錄按鈕觸發事件
submitForm: function(formName) {
// 驗證輸入框是否為空
this.$refs[formName].validate((valid) => {
if (valid) {
document.loginform.submit()
}
})
}
}
})
</script>
</html>
定義訪問登錄頁介面:
/**
* 登錄頁面
* @return
*/
@RequestMapping("login.html")
public String login() {
return "login";
}
配置類修改:
package com.lmc.security.conf;
import org.springframework.context.annotation.Configuration;
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.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
/**
* @author lmc
* @Description: TODO
* @Create 2021-10-23 19:55
* @version: 1.0
*/
@Configuration
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring()
.antMatchers("/assets/**")
.antMatchers("/403", "/404", "/500")
.antMatchers("/login.html");
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth
.inMemoryAuthentication() // 在記憶體存盤用戶資訊
.withUser("lmc").password("{noop}123") // 用戶名為lmc,密碼為123,{noop}表示密碼不加密
.roles("USER"); // 給用戶lmc賦予USER角色
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.anyRequest().authenticated() // 其他請求需要認證
.and()
.formLogin() // 通過form進行登錄
.loginPage("/login.html")
.loginProcessingUrl("/login")
.failureUrl("/500")
.and()
.csrf().disable(); // 關閉跨站請求偽造保護
}
}
此時,訪問 http://localhost:9040/test 會跳到 http://localhost:9040/login.html 頁面,如果賬號密碼輸入錯誤,就會跳到失敗頁面 http://localhost:9040/500 ,自定義登錄已完成,
源代碼在 gitee上 https://gitee.com/lmchh/lmc-tools, 可供參考
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/342116.html
標籤:其他
