SpringSecurity 学习笔记
Posted dingwen_blog
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringSecurity 学习笔记相关的知识,希望对你有一定的参考价值。
文章目录
一、基本概念
1.认证
用户认证就是判断一个用户的身份是否合法的过程,用户去访问系统资源时系统要求验证用户的身份信息,身份合法方可继续访问,不合法则拒绝访问。常见的用户身份认证方式有:用户名密码登录,二维码登录,手机短信登录,指纹认证等方式。
2.会话
用户认证通过后,为了避免用户的每次操作都进行认证可将用户的信息保证在会话中。会话就是系统为了保持当前用户的登录状态所提供的机制,常见的有基于session方式、基于token方式等。
3.授权
授权是用户认证通过根据用户的权限来控制用户访问资源的过程,拥有资源的访问权限则正常访问,没有权限则拒绝访问。
3.1 授权方式
RBAC
基于资源的访问控制
RBAC
基于资源的访问控制(Resource-Based Access Control)是按资源(或权限)进行授权。可扩展性好,当人员角色发生改变时无需修改代码。
RBAC
基于角色的访问控制
RBAC
基于角色的访问控制(Role-Based Access Control)是按角色进行授权
二、与Shiro
对比
1.Shiro
Apache 旗下的轻量级权限控制框架
- 轻量级。
Shiro
主张的理念是把复杂的事情变简单。针对对性能有更高要求
的互联网应用有更好表现 - 通用性
- 不局限于 Web 环境,可以脱离 Web 环境使用
- 在 Web 环境下一些特定的需求需要手动编写代码定制
2.Spring Security
Spring Security 是 Spring 家族中的一个安全管理框架
- 和 Spring 无缝整合
- 全面的权限控制
- 专门为 Web 开发而设计
- 旧版本不能脱离 Web 环境使用
- 新版本对整个框架进行了分层抽取,分成了核心模块和 Web 模块。单独
引入核心模块就可以脱离 Web 环境。
- 重量级
三、Spring Security
原理
Spring Security 本质是一个过滤器链
1.重点理解过滤器
-
FilterSecurityInterceptor
:是一个方法级的权限过滤器, 基本位于过滤链的最底部super.beforeInvocation(fi)
表示查看之前的 filter 是否通过fi.getChain().doFilter(fi.getRequest()
,fi.getResponse())
;表示真正的调用后台的服务ExceptionTranslationFilter
:是个异常过滤器,用来处理在认证授权过程中抛出的异常
-
UsernamePasswordAuthenticationFilter
:对/login 的 POST 请求做拦截,校验表单中用户 名,密码
2.UserDetailsService
完成用户名、密码、角色权限的认证服务接口
- 返回值
UserDetails
:系统默认的用户的主体 :用户信息 - 方法参数
username
:通过用户名查询用户信息
2.PasswordEncoder
Spring Security 的密码加密解密器
-
BCryptPasswordEncoder
:BCryptPasswordEncoder 是 Spring Security 官方推荐的密码解析器,是对bcrypt
强散列方法的具体实现。是基于 Hash 算法实现的单 向加密。可以通过 strength 控制加密强度,默认 10。 -
示例
/** * 密码编码器测试 */ @Test void bCryptPasswordEncoderTest(){ BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder(10); String encodePassword = bCryptPasswordEncoder.encode("123456"); log.info("加密之后的密码:{}",encodePassword); boolean matches = bCryptPasswordEncoder.matches("123456",encodePassword); log.info("密码比较结果:{}",matches); }
四、SpringSecurity Web
权限方案
1. 设置登录系统的账号、密码
默认用户名为:user,密码见项目启动之后的控制台打印。每次都不一样。
1.1 方式一:通过配置文件修改
security:
user:
name: dingwen
password: 123456
1.2 方式二: 通过配置类自定义内存用户
package com.dingwen.spsest.config;
import com.dingwen.spsest.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.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
/**
* security 配置类
*
* @author dingwen
* 2021.05.17 15:31
*/
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
/**
* 用户详细信息服务
*
* @return {@link UserDetailsService}
*/
@Bean
public UserDetailsService userDetailsService() {
// 内存用户测试
InMemoryUserDetailsManager inMemoryUserDetailsManager = new InMemoryUserDetailsManager();
// 逗号分隔的字符串到授权列表
List<GrantedAuthority> authorityList = Authority Utils.commaSeparatedStringToAuthorityList("admin,user");
// 内存用户
inMemoryUserDetailsManager.createUser(User.withUsername("dingwen")
.password(bCryptPasswordEncoder()
.encode("123456"))
.authorities(authorityList)
.build());
return inMemoryUserDetailsManager;
}
/**
* 配置
*
* @param http http
* @throws Exception 异常
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
http.formLogin() //表单登录
.and()
.authorizeRequests() // 认证配置
.anyRequest() // 任何请求
.authenticated(); // 都需要认证通过
}
}
2.实现数据库认证来完成用户登录
项目结构
2.1 整合Mybatis-plus
2.1.1 maven 依赖
<?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.4.5</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.dingwen</groupId>
<artifactId>spring-security-study</artifactId>
<version>1.0</version>
<name>spring-security-study</name>
<description>spring-security-study</description>
<properties>
<java.version>1.8</java.version>
<mybatis.plus.boot.starter.version>3.4.2</mybatis.plus.boot.starter.version>
<mysql.connector.java.version>8.0.15</mysql.connector.java.version>
</properties>
<dependencies>
<!--boot-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!--web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--test-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--security-->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!--mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.connector.java.version}</version>
</dependency>
<!--mybatis - plus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis.plus.boot.starter.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
2.1.2 配置类
package com.dingwen.spsest.config;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* mybatis-plus 配置
*
* @author dingwen
* 2021.05.11 17:00
*/
@Configuration
// 注意此处配置了包扫描,无需再启动类中再配置
@MapperScan("com.dingwen.spsest.mapper")
public class MybatisPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor(){
MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
// 自动分页插件
mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
// 防止全表更新、删除插件
mybatisPlusInterceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());
return mybatisPlusInterceptor;
}
}
2.1.3 项目配置文件配置
spring:
datasource:
url: jdbc:mysql://192.168.233.128:3306/spring-security-study?characterEncoding=utf-8&useSSL=true&serverTimezone=GMT
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: 123456
mybatis-plus:
configuration:
map-underscore-to-camel-case: true
global-config:
banner: false
mapper-locations: classpath*:mapper/*.xml
logging:
config: classpath:log/logback-spring.xml
2.2 准备SQL
-- 用户表
-- 密码: 123456
create table if not exists `sss_user`(
`id` bigint primary key auto_increment comment '用户表主键ID',
`username` varchar(20) unique not null comment '用户名',
`password` varchar(100) not null comment '密码'
)engine=innodb auto_increment=10 default charset=utf8 comment='用户表';
insert into `sss_user` (`username`,`password`) values
('admin','$2a$10$U1Ef55PpIqaUHBqmip8Lc.ld22RtMRtNEsVFLk0kw5XTIBCAm84Eu'),
('xiaoming','$2a$10$U1Ef55PpIqaUHBqmip8Lc.ld22RtMRtNEsVFLk0kw5XTIBCAm84Eu'),
('lihua','$2a$10$U1Ef55PpIqaUHBqmip8Lc.ld22RtMRtNEsVFLk0kw5XTIBCAm84Eu'),
('lucy','$2a$10$U1Ef55PpIqaUHBqmip8Lc.ld22RtMRtNEsVFLk0kw5XTIBCAm84Eu');
2.3 准备用户实体
package com.dingwen.spsest.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import java.io.Serializable;
/**
* 用户实体
*
* @author dingwen
* 2021.05.18 11:07
*/
@Getter
@Setter
@ToString
@TableName(value = "sss_user")
public class UserEntity implements Serializable {
private static final long serialVersionUID = 9127742447760082647L;
/**
* id
*/
@TableId(value = "id",type = IdType.AUTO)
private Integer id;
/**
* 用户名
*/
@TableField(value = "username")
private String username;
/**
* 密码
*/
@TableField("password")
private String password;
}
2.4 用户查询服务
package com.dingwen.spsest.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.dingwen.spsest.entity.MenuEntity;
import com.dingwen.spsest.entity.RoleEntity;
import com.dingwen.spsest.entity.UserEntity;
import com.dingwen.spsest.mapper.MenuMapper;
import com.dingwen.spsest.mapper.RoleMapper;
import com.dingwen.spsest.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
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.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
/**
* user service impl
*
* @author dingwen
* 2021.05.18 11:15
*/
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
/**
* 用户映射器
*/
private final UserMapper userMapper;
@Autowired
public UserDetailsServiceImpl(UserMapper userMapper) {
this.userMapper = userMapper;
}
/**
* 根据用户名查询用户
*
* @param username 用户名
* @return {@link UserDetails}
* @throws UsernameNotFoundException 找不到用户
*/
@Override
public UserDetails loadUserByUsername(String username)以上是关于SpringSecurity 学习笔记的主要内容,如果未能解决你的问题,请参考以下文章
SpringSecurity学习笔记:搭建最简单的SpringSecurity应用
SpringSecurity - 学习笔记 - 会话管理之并发控制:同一账号只允许在一个设备登录
SpringSecurity - 学习笔记 - 会话管理之并发控制:同一账号只允许在一个设备登录
[原创]java WEB学习笔记61:Struts2学习之路--通用标签 property,uri,param,set,push,if-else,itertor,sort,date,a标签等(代码片段