系列文章目录
文章目录
前言
本文我们要讲述:
Spring Security
在下攸攸太上,是有人类女友的外星来客。
一、Spring Security介绍
1. Spring Security介绍
Spring Security是一个功能强大且灵活的安全框架,用于在Spring应用程序中提供身份验证、授权、保护和攻击防护等安全功能。它提供了大量的配置选项和扩展机制,使开发者能够轻松地集成和定制应用程序的安全需求。
2. Spring Security特性
身份验证(Authentication):Spring Security提供了多种身份验证机制,包括基于表单、HTTP基本认证、OpenID、LDAP、OAuth等。它还支持自定义身份验证逻辑,可以轻松地集成其他身份验证提供者。
授权(Authorization):Spring Security提供了强大的授权机制,可以根据用户的身份和角色来限制其对资源的访问。它支持基于角色和权限的访问控制,可以在代码和配置中定义细粒度的安全规则。
攻击防护(Attack Protection):Spring Security提供了多种防护机制,用于防范常见的安全威胁,如跨站脚本攻击(XSS)、跨站请求伪造(CSRF)、点击劫持、会话管理等。它使用了各种技术和策略,如CSRF令牌、XSS过滤器、会话管理器等来保护应用程序的安全性。
安全事件和日志(Security Events and Logging):Spring Security提供了丰富的安全事件和日志功能,可以记录和跟踪与安全相关的事件。开发者可以基于这些事件和日志来监控和审计应用程序的安全性,并对可能的安全威胁做出响应。
扩展性和定制化(Extensibility and Customization):Spring Security提供了灵活的配置选项和扩展机制,使开发者能够根据应用程序的特定需求进行定制。开发者可以编写自定义的认证和授权逻辑,实现自定义的安全过滤器和拦截器,以满足应用程序的特定安全需求。
本文将着重介绍身份验证、授权的特性。
二、Spring Security代码编写:第一阶段
1. 创建Maven项目
2. 在pom文件里添加依赖
<dependencies>
<!-- SpringBoot -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.1.10.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!-- SpringMVC -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.1.10.RELEASE</version>
</dependency>
<!-- Thymeleaf -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
<version>2.1.10.RELEASE</version>
</dependency>
<!--Spring Security-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
<version>2.1.10.RELEASE</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.7.0</version>
</dependency>
<!--json-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.60</version>
<scope>compile</scope>
</dependency>
</dependencies>
3. 目录结构
4. SecurityController文件
@Controller
public class SecurityController {
@RequestMapping("/main")
public String main(){
return "main";
}
}
5. 配置SpringBootMain文件
@SpringBootApplication
public class SpringBootMain {
public static void main(String[] args) {
SpringApplication.run(SpringBootMain.class, args);
}
}
6. application.yml文件
server:
port: 8181
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password:
url: jdbc:mysql://localhost:3308/security
7. main.html文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>main</title>
</head>
<body>
<h1>我是Main页面!我要刺杀老师!喝啊!</h1>
<a href="/logout">退出登录</a>
</body>
</html>
8. 启动项目
打开网页,输入:127.0.0.1:8080
做一次页面点击后,控制台输出密码
我们输入固定的用户名:user与密码:891de0f8-256c-4f74-ba5b-999f25531c82可进行登录,登录后在地址栏可以访问/main页面
没有登录前,会自动跳转到/login页面,因为没有做登录之前,程序不让进行其他操作(第二阶段可改)
我们其实登录都是使用自定义的用户名和密码,于是乎,我们如何自定义用户名、密码呢?且听下回分解
三、Spring Security代码编写:第二阶段
欸!我就是下回诶!
1. 第二阶段第三阶段总体结构图
2. 在pom文件里添加依赖
<!-- 4.添加mysql数据库的驱动包依赖-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.30</version>
</dependency>
<!-- 3.添加Mybatis 的启动器依赖-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.1</version>
</dependency>
3. SecurityConfig文件
package com.jjy.config;
import com.jjy.service.impl.UserDetailsServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
// 表单认证
http.formLogin()
.loginProcessingUrl("/login")
//此处表单不写action属性也可以 //当发现/login时认为是登录,需要执行UserDetailsServiceImpl
.successForwardUrl("/main") //此处是post请求
.loginPage("/login.html");
// url 拦截
http.authorizeRequests()
.antMatchers("/login.html").permitAll() //login.html不需要被认证
.anyRequest().authenticated();//所有的请求都必须被认证。必须登录后才能访问。
//关闭csrf防护
http.csrf().disable();
}
@Bean // 是Bean标签,security框架要求密码必须是加密的处理,
public PasswordEncoder getPwdEncoder(){
return new BCryptPasswordEncoder();
}
}
4. UserDetailsServiceImpl文件
自定义用户名为admin,密码为123
package com.jjy.service.impl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
@Autowired
private PasswordEncoder passwordEncoder;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// 1. 查询数据库判断用户名是否存在,如果不存在抛出UsernameNotFoundException
if (!username.equals("admin")){
throw new UsernameNotFoundException("用户名不存在");
}
// 把查询出来的密码进行解析,或者直接把password放到构造方法中
String password = passwordEncoder.encode("123");
return new User(username, password, AuthorityUtils.commaSeparatedStringToAuthorityList("admin"));
}
}
5. login.html文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/login" method="post">
用户名:<input type="text" name="username"><br/>
密码:<input type="password" name="password"><br/>
<input type="submit" value="提交">
</form>
</body>
</html>
6. 实现效果
四、Spring Security代码编写:第三阶段
记忆功能实现
1. RememberMeConfig文件
package com.jjy.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
import javax.sql.DataSource;
@Configuration
public class RememberMeConfig {
@Autowired
private DataSource dataSource;
@Bean
public PersistentTokenRepository getPersistentTokenRepository() {
JdbcTokenRepositoryImpl jdbcTokenRepositoryImpl=new JdbcTokenRepositoryImpl();
jdbcTokenRepositoryImpl.setDataSource(dataSource);
//自动建表,第一次启动时需要,第二次启动时注释掉
//jdbcTokenRepositoryImpl.setCreateTableOnStartup(true); //该代码不好用,使用sql手动创建库,表
return jdbcTokenRepositoryImpl;
}
}
2. SecurityConfig文件
package com.jjy.config;
import com.jjy.service.impl.UserDetailsServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
PersistentTokenRepository repository;
// private RememberMeConfig repository;
@Autowired
private UserDetailsServiceImpl userDetailsService;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.rememberMe()
.userDetailsService(userDetailsService) //登录逻辑交给哪个对象
.tokenRepository(repository); //持久层对象
// 表单认证
http.formLogin()
.loginProcessingUrl("/login")
//此处表单不写action属性也可以 //当发现/login时认为是登录,需要执行UserDetailsServiceImpl
.successForwardUrl("/main") //此处是post请求
.loginPage("/login.html");
// url 拦截
http.authorizeRequests()
.antMatchers("/login.html").permitAll() //login.html不需要被认证
.anyRequest().authenticated();//所有的请求都必须被认证。必须登录后才能访问。
//关闭csrf防护
http.csrf().disable();
}
@Bean // 是Bean标签,security框架要求密码必须是加密的处理,
public PasswordEncoder getPwdEncoder(){
return new BCryptPasswordEncoder();
}
}
3. login.html更改
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/login" method="post">
用户名:<input type="text" name="username"><br/>
密码:<input type="password" name="password"><br/>
<input type="checkbox" name="remember-me" value="true"/>记住我 <br/>
<input type="submit" value="提交">
</form>
</body>
</html>
4. 创建数据库和表
创建security数据库
创建表的语句
create table persistent_logins (
username varchar(64) not null,
series varchar(64) primary key,
token varchar(64) not null,
last_used timestamp not null)
5. 实现效果
输入如下属性,点击记住我(密码123),之后提交
可以自动跳转页面,同时数据库新增一条数据
点击退出登录后,数据库内数据被清除
五、Spring Security代码编写:第四阶段
本阶段主要做权限验证
1. 项目结构
2. 在pom文件里添加依赖
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity5</artifactId>
<version>3.0.4.RELEASE</version>
</dependency>
3. 新增show.html界面
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity5">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>获得springsequraty属性</h1>
用户名:<span sec:authentication="name">123</span><br/>
用户名:<span sec:authentication="principal.username">456</span><br/>
凭证:<span sec:authentication="credentials">456</span><br/>
权限和角色:<span sec:authentication="authorities">456</span><br/>
客户端地址:<span sec:authentication="details.remoteAddress">456</span><br/>
sessionId:<span sec:authentication="details.sessionId">456</span><br/>
通过权限判断:
<button sec:authorize="hasAuthority('/insert')">新增</button>
<button sec:authorize="hasAuthority('/delete')">删除</button>
<button sec:authorize="hasAuthority('/update')">修改</button>
<button sec:authorize="hasAuthority('/select')">查看</button>
<br/>
通过角色判断:
<button sec:authorize="hasRole('abc')">新增</button>
<button sec:authorize="hasRole('abc')">删除</button>
<button sec:authorize="hasRole('abc')">修改</button>
<button sec:authorize="hasRole('abc1')">查看</button>
</body>
</html>
4. 更新UserDetailsServiceImpl文件返回值
return new User(username, password, AuthorityUtils.commaSeparatedStringToAuthorityList("admin,/select,/insert,ROLE_abc1"));
赋予admin用户select、insert的权限,归于ROLE_abc1组下
5. 实现效果
登录后,我们通过地址栏输入http://127.0.0.1:8181/show进入show页面,可以发现,用户admin的权限只有新增、查看两项。
六、CSRF
CSRF(Cross-Site Request Forgery,跨站请求伪造)是一种常见的网络攻击技术,攻击者通过伪造用户的请求来实现非法操作,利用用户在目标网站上的身份和权限。
CSRF攻击通常发生在存在用户身份验证的应用程序中。攻击者诱使受害者在已经登录过的网站上执行恶意操作,从而利用受害者的权限进行非法操作。攻击者可以通过各种手段,如发送伪造的电子邮件、社交工程攻击、恶意广告等来引诱受害者点击恶意链接或访问恶意网站。
Spring Security是一个功能强大的安全框架,提供了多种功能来保护应用程序免受常见的安全威胁,包括 CSRF攻击。在Spring Security中,默认情况下启用了CSRF防护。
CSRF防护是通过生成和验证一个CSRF令牌来实现的。这个令牌通常是一个随机生成的字符串,由服务器在用户访问页面时生成,并将其附加到表单或请求的头部中。在用户提交请求时,服务器会验证请求中的令牌是否与服务器生成的令牌匹配,如果不匹配,服务器将拒绝该请求,防止CSRF攻击的发生。
在Spring Security配置中,可以使用csrf()方法来配置和启用CSRF防护。默认情况下,Spring Security会生成一个CSRF令牌,并将其附加到表单和请求头中。如果需要关闭CSRF防护,可以使用http.csrf().disable()方法来禁用它。
总结起来,CSRF是一种常见的网络攻击技术,Spring Security提供了默认的CSRF防护来保护应用程序免受此类攻击。
总结
本文讲述了:
Spring Security:进行身份验证、授权功能的框架
在下攸攸太上,如果把我的生活拍成电视剧,那将会是《来自行星的高级生命体》。