[!quote] 授权 授权 是当用户请求一个受保护的资源时,会检查用户是否已登录,以及用户是否具有访问该资源的权限
mermaid
sequenceDiagram
participant K AS 客户端
participant J AS jwt认证过滤器
participant R AS redis
participant S AS SecurityContextHolder
K->>J: 携带token发起请求
J->>J: 获取,解析token,获取到userId
J->>R: 携带userId
R->>J: 返回用户信息
J->>S: 把用户信息存入到SecurityContextHolder,便于后续的过滤器可以使用用户信息做某些事情[!quote] Spring Security 的两种授权模式:
- 用户 - 权限 - 资源:用户直接与权限进行关联,权限再与资源进行关联,适用于权限管理相对较少复杂的系统
- 用户 - 角色 - 权限 - 资源:用户首先与角色进行关联,角色再与权限进行关联,权限再与资源进行关联,适用于权限管理较为复杂的系统
[!quote] Spring Security 的两种授权方法:
- 基于请求的授权
- 基于方法的授权
[!hint] 一般使用 用户 - 角色 - 权限 - 资源 + 基于方法的授权 来进行授权
基于请求的授权
[!quote] 基于请求的授权 基于请求的授权 是指根据 HTTP 请求的信息 来决定是否授予访问权限,适合权限规则相对简单,与具体操作无关的情况
基于请求的授权策略通常适用于权限规则相对简单,与具体操作无关的情况。例如,一个博客网站可能只需要根据 URL 就能决定用户是否可以访问某个页面。因为在这种情况下,权限规则通常可以直接映射到 URL 路径上,例如“/admin”路径只允许管理员访问
基于方法的授权
[!quote] 基于方法的授权 基于方法的授权 是指在执行特定方法前先检查用户是否具有相应的权限,适用于复杂的权限控制
用户 - 权限 - 资源
……
用户 - 角色 - 权限 - 资源
数据库表
mermaid
erDiagram
USER ||--o{ USER_ROLE : has
ROLE ||--o{ USER_ROLE : has
ROLE ||--o{ ROLE_AUTHORITY : has
AUTHORITY ||--o{ ROLE_AUTHORITY : has
USER {
user_id INT
user_name VARCHAR(50)
user_password VARCHAR(100)
}
ROLE {
role_id INT
role_name VARCHAR(30)
role_description VARCHAR(50)
}
AUTHORITY {
authority_id INT
authority_name VARCHAR(20)
authority_description VARCHAR(50)
}
USER_ROLE {
user_role_id INT
user_id INT
role_id INT
}
ROLE_AUTHORITY {
role_authority_id INT
role_id INT
authority_id INT
}[!quote]+ 具体的建表语句
sql// 用户表 user create table user ( user_id int auto_increment primary key, user_name varchar(50) not null, user_password varchar(100) not null );sql// 角色表 create table role ( role_id int auto_increment primary key, role_name varchar(30) not null comment '角色名', role_description varchar(50) not null comment '描述角色' ) comment '角色表';sql// 权限表 create table authority ( authority_id int auto_increment, authority_name varchar(20) not null, authority_description varchar(50) not null comment '权限描述', constraint authority_pk primary key (authority_id) ) comment '权限表';sql// 用户-角色表 create table user_role ( user_role_id int auto_increment, user_id int not null, role_id int not null, constraint user_role_pk primary key (user_role_id) ) comment '用户角色关联表';sql// 角色-权限表 create table role_authority ( role_authority_id int auto_increment, role_id int not null, authority_id int not null, constraint role_authority_pk primary key (role_authority_id) ) comment '角色权限关联表';
具体操作
- 在
SecurityConfig配置中开启基于方法授权
java
@Configuration
@EnableMethodSecurity
public class SecurityConfig { …… }- 对于请求未授权的接口【
USER 角色请求了 ADMIN 的接口】的处理
java
// 在配置文件 SecurityConfig 中配置
http.exceptionHandling(exceptionHandling -> {
exceptionHandling
.accessDeniedHandler(new MyAccessDeniedHandler());
});java
package com.example.spring_security.infrastructure.config;
public class MyAccessDeniedHandler implements AccessDeniedHandler {
Logger logger = LoggerFactory.getLogger(MyAccessDeniedHandler.class);
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
logger.atInfo().log("你没有权限访问,我要做一些处理");
String localizedMessage = accessDeniedException.getLocalizedMessage();
Result<String> responseResult = Result.buildResult(Result.Status.ERROR, "你没有权限访问", localizedMessage);
ObjectMapper objectMapper = new ObjectMapper();
String jsonData = objectMapper.writeValueAsString(responseResult);
response.setContentType("application/json;charset=utf-8");
response.getWriter().println(jsonData);
}
}- 在方法上添加授权注解
java
// 创建用户
@PostMapping("/create")
// 在访问接口之前,检查用户是否是 USER 或者 ADMIN
@PreAuthorize("hasRole('USER') || hasRole('ADMIN')")
public Result createUser(@RequestBody LoginUser loginUser) {
userService.createUser(loginUser);
return Result.buildResult(Result.Status.OK, "创建用户成功");
}
// 查询所有用户数据
@GetMapping("/users")
@PreAuthorize("hasRole('ADMIN')")
public Result getAllUsers() {
return Result.buildResult(Result.Status.OK, "查询所有用户数据成功");
}