SpringSecurity入门详解
1.安全框架概述
什么是安全框架?解决系统安全问题的框架,如果没有安全框架,我们需要手动处理每个资源的访问控制,非常麻烦。使用安全框架,我们可以通过配置的方式实现对资源的访问控制
2.SpringSecurity:Spring家族的一员,是一个能够基于Spring的企业应用系统提供声明式的安全访问控制解决方案、高度自定义的安全框架。它提供了一组可以在Spring应用上下文中配置的Bean,充分利用了Spring IOC(控制反转Inversion of Control ),DI(Dependency Injecion 依赖注入)和AOP(面向切面编程)功能,为应用系统提供声明示的安全访问控制功能,减少了为企业系统安全控制编写大量重复代码的工作。它的核心功能是“认证”和“授权”。
2.入门demo
1.导入依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.0.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>spingsecurity-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spingsecurity-demo</name>
<description>spingsecurity-demo</description>
<properties>
<java.version>15</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Spring Security已经被Spring boot进行集成,使用时直接引入启动器即可。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
2.访问页面
导入spring-boot-starter-security启动器后,Spring Security已经生效,默认拦截全部请求,如果用户没有登录,跳转到内置登录页面。
在浏览器中输入http://localhost:8080/login
默认的username是user,默认的随机密码password会打印在控制台(每次项目启动后的密码都不同)
3.可以自己自定义当前页面的用户名和密码
通过修改application.properties(application.yml)配置文件
spring.security.user.name=yiailuo
spring.security.user.password=yiailuo
4.UserDetailsService详解
如果需要自定义登录逻辑需要实现UserDetailsService接口,它的返回值是UserDetails
也是一个接口
13~25行分别对应获取权限,获取密码,获取用户名,账号是否过期,账号是否被锁定,密码是否过期,是否可用
5.PasswordEncoder详解
encode() :把参数按照特定的解析规则进行解析。
matches() :验证从存储中获取的编码密码与编码后提交的原始密码是否匹配。如果密码
匹配,则返回 true;如果不匹配,则返回 false。第一个参数表示需要被解析的密码。第二
个参数表示存储的密码。
upgradeEncoding() :如果解析的密码能够再次进行解析且达到更安全的结果则返回
true,否则返回 false。默认返回 false。
6.自定义登录逻辑
- 自定义逻辑
package com.example.demo.service;
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 UserDetailServiceImpl implements UserDetailsService {
//自定义登录逻辑
@Autowired
private PasswordEncoder passwordEncoder;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
System.out.println("执行自定义登陆逻辑");
//1.根据用户名去数据库查询,如果不存在抛出UserNameNotFound异常(正常情况下是去数据库查)
//这里只是一个简单demo
if(!"yiailuo".equals(username)){
throw new UsernameNotFoundException("用户名不存在");
}
//2.比较密码(注册时已经加密过的,如果匹配成功返回UserDetails)
String password = passwordEncoder.encode("yiailuo");
return new User(username,password, AuthorityUtils.commaSeparatedStringToAuthorityList("admin,normal,ROLE_abc"));//权限列表
}
}
这里UserDetails实现User类的实例
导包时注意!!!
User类属于import org.springframework.security.core.userdetails.User;并不是自己创建的User类。
有两个构造方法任选其一即可
- 自定义登录页面
登录页面login.html等静态资源放在resource/static文件夹里
login.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<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>
登录成功的页面main.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>登陆成功</title>
</head>
<body>
登陆成功!!!<a href="main1.html">跳转</a>
</body>
</html>
登录失败的页面error.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
操作失败,请重新登录<a href="/login.html">跳转</a>
</body>
</html>
编写配置类
WebSecurityConfigurerAdapter在SpringSecurity5.7版本弃用了
package com.example.demo.config;
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;
@Configuration
//自定义登录页
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http)throws Exception{
//登录
http.formLogin()
//自定义登录页面
.loginPage("/login.html")
//必须和表单提交的接口一致
.loginProcessingUrl("/login")
//登录成功跳转的页面,POST请求
.successForwardUrl("/toMain")
//登录失败后跳转的页面,post请求
.failureForwardUrl("/toError")
//授权
http.authorizeRequests()
//放行login.html,无需验证
.antMatchers("/login.html").permitAll()
//放行error.html,无需验证
.antMatchers("/error.html").permitAll()
//所有请求都必须通过认证才能访问,(必须登录)
.anyRequest().authenticated();//拦截所有请求,有先后顺序,anyRequest()放在最后
//关闭csrf防护
http.csrf().disable();
}
@Bean
public PasswordEncoder getPw(){
return new BCryptPasswordEncoder();
}
}
编写控制器
package com.example.demo.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class LoginController {
@RequestMapping("/toMain")
public String main(){
return "redirect:main.html";
}
@RequestMapping("/toError")
public String error(){
return "redirect:error.html";
}
}
启动项目后的登录页面
输入设置好的用户名和密码,点击登录即可跳转到main.html,输入错误的用户名和密码即可跳转到error.html