Spring Security入门基础
Posted 李奈 - Leemon
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring Security入门基础相关的知识,希望对你有一定的参考价值。
Spring Security入门基础
文章目录
一,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].以上是关于Spring Security入门基础的主要内容,如果未能解决你的问题,请参考以下文章
Spring Security入门(2-2)Spring Security 的运行原理 2
Spring Security OAuth2入门级使用(示例)
Spring Security OAuth2入门级使用(示例)