Skip to content

[!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, "查询所有用户数据成功");  
}