- 前言
- 一、创建项目
- 二、认证登录
- 1.默认账号密码
- 2.自定义账号密码
- 三、关于授权
- 四、关于Session
- 五、项目演示
前言
Spring Security是一款主要解决了认证和授权相关处理的安全框架
认证: 验证用户身份, 例如在登录过程中验证用户名与密码是否匹配
授权: 使得用户允许访问服务器的某些资源, 或禁止访问某些资源
Spring Boot Security是在Spring Boot中使用的, 基于Spring Security的依赖项, 其本质就是Spring Security框架加上了应用于Spring Boot工程的自动配置
提示:以下是本篇文章正文内容,下面案例可供参考
新建Spring Boot项目, 勾选spring web和spring security
项目中Spring Security 在
pom.xml
中的依赖为
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-securityartifactId>
dependency>
添加依赖后, 启动项目, 在访问所有资源时, 都是要求先登录(未退出之前只需要登录1次)
二、认证登录 1.默认账号密码默认的用户名是user
, 密码会在启动时输出在控制台, 例如: 启动项目
2.自定义账号密码Useing generated security password: 密码, ":"后面就是密码, 账号为user, 浏览器访问
http://localhost:8080/
, 输入账号密码
在项目默认配置文件application.properties中配置以下信息:
#配置security登录账号
spring.security.user.name=root
#配置security登录密码
spring.security.user.password=root
此时security登录账号密码为 : root/root, 控制台不在输出随机密码
bcrypt加密
使用security验证密码时, 密码必须被框架中的BCryptPasswordEncoder类加密过
Security框架推荐使用Bcrypt算法对密码原文进行加密处理, 框架中有BCryptPasswordEncoder类, 此类可以实现加密, 判断密码是否匹配等功能, 使用的是Bcrypt算法, 每次加密后密文都不相同, 因为里面加入了随机盐值, 盐值保存在密文中, 也可以正常验证
验证方法
Spring Security会在处理登录认证时自动根据尝试登录的用户名调UserDetailsService
接口实现类的loadUserByUsername
此方法, 并获得UserDetails
对象, 此对象包含用户的密码、权限等信息, 接下来, Spring Security会自动判断密码, 如果不正确, 将返回错误信息, 如果正确, 会将此用户信息(包含权限)保存下来(默认保存在Session中)
三、关于授权比对密码时, 对比的密文必须经过Bcrypt算法加密
需要在security配置类上, 添加
@EnableGlobalMethodSecurity(perPostEnabled=true)
注解, 启用控制器处理方法权限验证
并让security配置类继承
WebSecurityConfigurerAdapter
(web安全配置适配器), 并重写configure(HttpSecurity http)
方法, 里面配置关闭跨域(前后端分离), 启用登录时表单验证和配置对需要的请求验证, 不需要验证的请求直接通过
四、关于Session最后在控制器方法上添加
@PerAuthorize("hasAuthority('权限')")
注解, 否则访问会出现403没有权限访问的错误
HTTP协议是一种无状态的协议, 同一个用户在同一台设备上多次对同一个服务器端进行访问时, 默认在服务器端并不保存此用户的相关信息, 所以, 无论访问多少次, 服务器端都无法识别用户的身份
为了解决服务器端可以识别用户身份, 可以使用Session(会话), 当某个客户端第1次向服务器端发送请求后, 服务器端会在其内存中保存此用户的信息, 并且此信息会关联到一个唯一的SessionI D, 当服务器端进行响应时, 会将此Session ID响应到客户端, 后续, 客户端应该在每次请求时都携带此Session ID, 则服务器可以根据后续请求头中的Session ID对应到此前保存的数据, 从而识别用户身份
使用Session保存的数据, 通常是:
- 用户身份的唯一标识, 例如用户的ID
- 高频率使用的数据, 例如用户的权限(有数据不一致的风险)
- 不便于使用其他存储技术进行处理的数据
Session消失的机制
- 超时, 如果客户端长时间未向服务器端发起任何请求, 则在服务器端上, 此客户端对应的Session数据会被清除, 常见的设置值是15分钟或30分钟
- 更换客户端(包括更换浏览器、关开浏览器), 也会无法访问此前的Session数据, 并且, 此前的Session数据将会根据超时机制被清理
- 服务器端设备关机或重启
- 服务器端程序调用了清除Session数据的方法, 例如调用了
HttpSession
对象的invalidate()
方法
新建
VO
类, 模拟从数据库查询到的数据信息
package cn.qingtian.security.pojo.vo;
import java.util.List;
public class UserLoginVO {
// 用户名
private String username;
// 密码
private String password;
// 模拟用户权限
private List<String> permissions;
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public List<String> getPermissions() {
return permissions;
}
public void setPermissions(List<String> permissions) {
this.permissions = permissions;
}
}
配置Security配置类
package cn.qingtian.security.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
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;
// Security配置类, 用户生成BCryptPasswordEncoder实例
@Configuration
// security中启用全局方法安全, 用于限制User对控制器方法的访问权限
@EnableGlobalMethodSecurity(prePostEnabled = true)
// 此类继承web安全配置适配器
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
// 实现WebSecurityConfigurerAdapter中的configure方法
@Override
protected void configure(HttpSecurity http) throws Exception {
//配置不需要登录就能访问的请求
String[] url = {};
// 关闭跨域
http.csrf().disable();
// 配置授权请求
http.authorizeRequests()
// 配置不需要登录就能访问的请求
.antMatchers(url).permitAll()
// 除不需要登录就能访问的请求外, 所有请求都需要登录和授权
.anyRequest().authenticated();
// 当登录时启用表单登录
http.formLogin();
}
}
创建uril包, 实现全局密码加密
package cn.qingtian.security.util;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;
@Component
public class GlobalPasswordEncoder {
@Autowired
private PasswordEncoder passwordEncoder;
public String encode(String rowPassword){
return passwordEncoder.encode(rowPassword);
}
}
实现认证类
UserDetailsService
, 并重写loadUserByUsername
方法
package cn.qingtian.security.service.impl;
import cn.qingtian.security.pojo.vo.UserLoginVO;
import cn.qingtian.security.util.GlobalPasswordEncoder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
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.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
// security认证类
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
// 加载全局密码编码器
@Autowired
private GlobalPasswordEncoder globalPasswordEncoder;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// 模拟从数据持久层查询到的数据
UserLoginVO user = new UserLoginVO();
user.setUsername("admin");
// 此密码需要用Security中的BCrypt加密
String password = globalPasswordEncoder.encode("123456");
System.out.println(password);
user.setPassword(password);
// 模拟权限数据
List<String> permissions = new ArrayList<>();
permissions.add("/list");
user.setPermissions(permissions);
// 授予权限集合
List<GrantedAuthority> grantedAuthorities = new ArrayList<>();
for(String permission : permissions){
// GrantedAuthority实现类 SimpleGrantedAuthority
SimpleGrantedAuthority simpleGrantedAuthority = new SimpleGrantedAuthority(permission);
grantedAuthorities.add(simpleGrantedAuthority);
}
//判断方法传入登录用户名是否在数据库中
if (!user.getUsername().equals(username)){
// security验证中的异常 BadCredentialsException
throw new BadCredentialsException("用户名不存在");
}
// User为security中的类, 此类会被security保存在Session中(默认)
return User.builder()
.username(user.getUsername())
.password(user.getPassword())
// 授予权限
.authorities(grantedAuthorities)
// 账号是否过期
.accountExpired(false)
// 禁用
.disabled(false)
// 凭证是否过期
.credentialsExpired(false)
.build();
}
}
编写处理器方法
package cn.qingtian.security.controller;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
//控制器层
@RestController
public class UserController {
//@PreAuthorize("hasAnyAuthority(String...)")预授权注解(有任何权限)
@PreAuthorize("hasAnyAuthority('/list')")
@RequestMapping("/list")
public String list(){
return "用户列表获取成功";
}
}
最后启动效果
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)