Plan B: 根据《SpringBoot集成Spring Security(7)——认证流程》,我们知道如果我们要自定义一种登陆方式,那么 xxxProvider、xxxUserDetailsService、xxxToken,这三个应该是一体的。
provider 决定了调用哪个 userDetailsService,support() 方法决定了这个 provider 支持的 token。但是我在这个项目中偷了懒,我只自定义了 provider 和 userDetailsService,却没有定义专属的 token,而是使用 UsernamePasswordAuthenticationToken。
那么 UsernamePasswordAuthenticationToken 将会同时被我的 provider 以及 DaoAuthenticationProvider 所支持。那么 PlanB 就是定义一个专属的 token,跟《SpringBoot 集成 Spring Security(8)——短信验证码登录》一样。
这的确是一个好主意,而且我认为这也是 Spring Security 在非 DEMO 项目中正确的使用方式,provider + userDetailsService + token 三者捆绑。
然而这毕竟只是一个 demo,而且还是第三章的 demo,考虑到入手难度,不想引入太多的类。PASS!
Plan 3: 换个思路,这个 DaoAuthenticationProvider 是咋加进去,如果它不在 providers 循环中,不就没问题了?
修改 WebSecurityConfig 类如下图,去除了 auth .userDetailsService(userDetailsService).passwordEncoder(passwordEncoder())
配置,这会导致将 DaoAuthenticationProvider 加入到 providers 集合中。
我参考 https://blog.csdn.net/wzl19870309/article/details/70314085 这篇文章,找到了解决方案。
一、前言《SpringBoot 集成 Spring Security》系列文章,原本只是我自己学习后写的笔记,没想到受到大家的欢迎,能够对大家带来帮助,让我感到十分高兴。但说起来我也只是初学者,这一系列文章中可能也存在错误,本文是为了解决 UserNotFoundException 这个异常无法抛出而写出。这个问题大致是这样的,我们知道 Spring Security 的验证处理是由某个 Pr...
HomeTaskWeek3
实现Comparable []排序(Comparable []元素)方法。 它的任务是订购对象的数组,这些对象的类实现Comparable接口。 该方法必须返回该数组的副本,而原始数组必须保持完整。 给出一些对不同对象进行排序的示例。
测试String,StringBuffer和StringBuilder串联方法的性能。 使用System.currentTimeMillis()衡量执行时间
实现用户auth(字符串登录名,字符串密码)会抛出AuthException方法。 User类包含两个字段:字符串登录名和字符串密码。 该方法应在“数据存储”中通过登录名和密码搜索用户,然后将其返回。 如果找不到用户,则应引发UserNotFoundException。 如果用户名或密码为空-WrongCredentialsException。 如果通过登录找到用户,
这个教程是我在往项目中一点一点添加 Spring Security的过程的一个笔记,也是我学习 Spring Security的一个过程。
在解决这个问题之前要先说一点authentication-provider默认加载的是DaoAuthenticationProvider类。
完成了上一章的内容后在测试的时候发现在UserDetailsService中抛出的UsernameNotF
以下配置基于spring boot版本1.4.2.RELEASE,默认引入的spring security版本为4.1.3.RELEASE,页面模板采用thymeleaf。
在MyUserDetailsService实现了UserDetailsService接口以后,在重写的loadUserByUsername方法里验证用户名不存在时,我们会抛出一个UsernameNotFoundExceptio...
虽然这并非一个典型需求,但是把这个问题解决了,有助于加深大家对于 Spring Security 的理解。
因此,松哥打算撸一篇文章和大家稍微聊聊这个话题。
1. 问题再现
可能有小伙伴还不明白这个问题,因此我先稍微解释一下。
当我们登录失败的时候,可能用户名写错,也可能密码写错,但是出于安全考虑,服务端一般不会明确提示是用户名写错了还是密码写
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
private final UserService userService;
@Autowired
public UserDetailsServiceImpl(UserService userService) {
this.userService = userS
最近项目中在使用Spring Security的token;在做退出和验证时,出现UserNotFoundException 不抛出问题;
当用户不存在时,只会抛出BadCredentialsException,而不是UsernameNotFoundException;
首先debug断点跟踪
进入该断点行中,根据断点信息已知 providers 一共只有1个,当前的 providers 是 DaoAuthenticationProvider
protected final U..
UserDetailsService代码如下,当根据用户名查询不到用户信息时,我们抛出UsernameNotFoundException异常,按预想,只需要捕获该异常,便可在用户名错误时给用户提示
然而实际运行时,无论用户名不存在还是密码错误,均抛出BadCredentialsException异常,无法知道登录失败的具体原因,经过debug发现,正是在这个地方做了判断,当hideUserNotFoundExceptions为false时,抛出UserNotFoundException异常,而true时.
通过查看源码可得知:
1. 前言
抽象类中AbstractUserDetailsAuthenticationProvider 接口抛出异常AuthenticationException
下面源码注释这么描述
* @throws AuthenticationException if the credentials c...
这是一个基于Spring Boot、Spring Security、JWT和OAuth2的示例项目,实现了用户注册、登录、注销、刷新令牌、访问受保护资源等功能。
## 技术栈
- Spring Boot 2.5.4
- Spring Security 5.5.1
- Spring Data JPA 2.5.4
- MySQL 8.0.26
- JWT 0.11.2
- OAuth2 2.5.4
- Lombok 1.18.20
## 数据库配置
在MySQL数据库中新建一个名为`springboot_security_jwt_oauth2`的数据库,执行以下SQL语句创建用户表:
```sql
CREATE TABLE `user` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`username` varchar(255) NOT NULL COMMENT '用户名',
`password` varchar(255) NOT NULL COMMENT '密码',
`enabled` tinyint(1) NOT NULL DEFAULT '1' COMMENT '是否启用',
`create_time` datetime NOT NULL COMMENT '创建时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_username` (`username`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COMMENT='用户表';
## 项目结构
├── src/main/java
│ └── com
│ └── example
│ └── demo
│ ├── DemoApplication.java
│ ├── config
│ │ ├── JwtConfig.java
│ │ ├── MyPasswordEncoder.java
│ │ └── SecurityConfig.java
│ ├── controller
│ │ ├── LoginController.java
│ │ └── UserController.java
│ ├── dao
│ │ ├── UserRepository.java
│ │ └── UserRoleRepository.java
│ ├── entity
│ │ ├── User.java
│ │ └── UserRole.java
│ ├── exception
│ │ ├── JwtAuthenticationException.java
│ │ └── UserNotFoundException.java
│ ├── service
│ │ ├── AuthService.java
│ │ ├── UserService.java
│ │ └── impl
│ │ ├── AuthServiceImpl.java
│ │ └── UserServiceImpl.java
│ ├── util
│ │ ├── JwtTokenUtil.java
│ │ └── JwtUserDetailsService.java
│ └── web
│ ├── JwtAuthenticationEntryPoint.java
│ ├── JwtAuthenticationFilter.java
│ ├── JwtAuthorizationFilter.java
│ ├── RestResponse.java
│ └── UserNotFoundExceptionHandler.java
└── src/main/resources
├── application.properties
├── static
└── templates
- `config`:Spring Security和JWT的配置类
- `controller`:控制器类,处理请求和响应
- `dao`:数据访问层,使用Spring Data JPA实现
- `entity`:实体类
- `exception`:异常类
- `service`:服务层接口和实现类
- `util`:工具类,包括JWT生成和解析、用户认证等
- `web`:Web相关类,包括异常处理、JWT过滤器等
## API文档
### 用户注册
- URL:`/api/register`
- Method:POST
- Request:
```json
"username": "test",
"password": "123456"
- Response:
```json
"code": 200,
"message": "注册成功",
"data": {
"id": 1,
"username": "test",
"password": "$2a$10$8uFJ3zZB.Sd7K3YB2K3Y/OfVhF4oJXeS3j0R2A3RG1c2UJWuXkSdC",
"enabled": true,
"createTime": "2021-10-01T08:16:28.000+00:00"
### 用户登录
- URL:`/api/login`
- Method:POST
- Request:
```json
"username": "test",
"password": "123456"
- Response:
```json
"code": 200,
"message": "登录成功",
"data": {
"accessToken": "eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJ0ZXN0Iiwicm9sZXMiOiJST0xFX1VTRVIiLCJleHAiOjE2MzI5NjQwODh9.5Syf8x3CZaLl0yHrXyXjJ4Qz4jJnVR3S4yIDg6GQ6puknFkJ9QWgJzJ5pB0tZzHfrGz2K1VJvJkHrOjLUQJWzA",
"tokenType": "Bearer",
"expiresIn": 3600
### 用户注销
- URL:`/api/logout`
- Method:POST
- Request Header:
Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJ0ZXN0Iiwicm9sZXMiOiJST0xFX1VTRVIiLCJleHAiOjE2MzI5NjQwODh9.5Syf8x3CZaLl0yHrXyXjJ4Qz4jJnVR3S4yIDg6GQ6puknFkJ9QWgJzJ5pB0tZzHfrGz2K1VJvJkHrOjLUQJWzA
- Response:
```json
"code": 200,
"message": "注销成功"
### 刷新令牌
- URL:`/api/refresh`
- Method:POST
- Request Header:
Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJ0ZXN0Iiwicm9sZXMiOiJST0xFX1VTRVIiLCJleHAiOjE2MzI5NjQwODh9.5Syf8x3CZaLl0yHrXyXjJ4Qz4jJnVR3S4yIDg6GQ6puknFkJ9QWgJzJ5pB0tZzHfrGz2K1VJvJkHrOjLUQJWzA
- Response:
```json
"code": 200,
"message": "刷新令牌成功",
"data": {
"accessToken": "eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJ0ZXN0Iiwicm9sZXMiOiJST0xFX1VTRVIiLCJleHAiOjE2MzI5NjQxMzQsImlhdCI6MTYzMjk2MDUzNH0.2hWq8dLJ7s9G6MqQ8Gg7kNvGzeOaJQFb4eBZ9RcB6N8lP3kglz8W_KXMh8r4oJZkzy5HOVZrB5YSEKNxZyY5lg",
"tokenType": "Bearer",
"expiresIn": 3600
### 获取当前用户信息
- URL:`/api/user/info`
- Method:GET
- Request Header:
Authorization: Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJ0ZXN0Iiwicm9sZXMiOiJST0xFX1VTRVIiLCJleHAiOjE2MzI5NjQxMzQsImlhdCI6MTYzMjk2MDUzNH0.2hWq8dLJ7s9G6MqQ8Gg7kNvGzeOaJQFb4eBZ9RcB6N8lP3kglz8W_KXMh8r4oJZkzy5HOVZrB5YSEKNxZyY5lg
- Response:
```json
"code": 200,
"message": "获取用户信息成功",
"data": {
"id": 1,
"username": "test",
"password": null,
"enabled": true,
"createTime": "2021-10-01T08:16:28.000+00:00",
"authorities": [
"authority": "ROLE_USER"
### 获取所有用户信息
- URL:`/api/user/all`
- Method:GET
- Response:
```json
"code": 200,
"message": "获取所有用户信息成功",
"data": [
"id": 1,
"username": "test",
"password": null,
"enabled": true,
"createTime": "2021-10-01T08:16:28.000+00:00",
"authorities": [
"authority": "ROLE_USER"
## 完整代码
完整代码请参考[GitHub](https://github.com/zhongshijun/springboot-security-jwt-oauth2)。