spring_security框架

spring security

Spring Security默认做了什么

  • 保护应用程序URL,要求对应用程序的任何交互进行身份验证。
  • 程序启动时生成一个默认用户“user”。
  • 生成一个默认的随机密码,并将此密码记录在控制台上。
  • 生成默认的登录表单和注销页面。
  • 提供基于表单的登录和注销流程。
  • 对于Web请求,重定向到登录页面;
  • 对于服务请求,返回401未经授权。
  • 处理跨站请求伪造(CSRF)攻击。
  • 处理会话劫持攻击。
  • 写入Strict-Transport-Security以确保HTTPS。
  • 写入X-Content-Type-Options以处理嗅探攻击。
  • 写入Cache Control头来保护经过身份验证的资源。
  • 写入X-Frame-Options以处理点击劫持攻击。

Spring Security 的底层原理

官方文档:Spring Security的底层原理

Spring Security之所以默认帮助我们做了那么多事情,它的底层原理是传统的Servlet过滤器

Filter

下图展示了处理一个Http请求时,过滤器和Servlet的工作流程:

filterchain

因此我们可以在过滤器中对请求进行修改或增强。

DelegatingFilterProxy

DelegatingFilterProxy 是 Spring Security 提供的一个 Filter 实现,可以在 Servlet 容器和 Spring 容器之间建立桥梁。通过使用 DelegatingFilterProxy,这样就可以将Servlet容器中的 Filter 实例放在 Spring 容器中管理。

delegatingfilterproxy

FilterChainProxy

复杂的业务中不可能只有一个过滤器。因此FilterChainProxy是Spring Security提供的一个特殊的Filter,它允许通过SecurityFilterChain将过滤器的工作委托给多个Bean Filter实例。

filterchainproxy

SecurityFilterChain

SecurityFilterChain 被 FilterChainProxy 使用,负责查找当前的请求需要执行的Security Filter列表。

image-20240606204656006

Multiple SecurityFilterChain

可以有多个SecurityFilterChain的配置,FilterChainProxy决定使用哪个SecurityFilterChain。如果请求的URL是/api/messages/,它首先匹配SecurityFilterChain0的模式/api/**,因此只调用SecurityFilterChain 0。假设没有其他SecurityFilterChain实例匹配,那么将调用SecurityFilterChain n。

image-20240606204803572

Spring Security数据库配置

官方文档:Java自定义配置

sql
create database if not exists spring_security;

use spring_security;

drop table if exists user;
create table if not exists user
(
    user_account  int primary key,
    user_name     varchar(255),
    user_password text
);

drop table if exists role;
create table if not exists role
(
    role_id   int primary key,
    role_name varchar(255)
);

drop table if exists permission;
create table if not exists permission
(
    peremission_id  int primary key,
    permission_name varchar(255)
);

drop table if exists user_role;
create table if not exists user_role
(
    user_account int,
    role_id      int,
    primary key (user_account, role_id),
    foreign key (user_account) references user (user_account),
    foreign key (role_id) references role (role_id)
);

drop table if exists role_permission;
create table if not exists role_permission
(
    role_id       int,
    permission_id int,
    primary key (role_id, permission_id),
    foreign key (role_id) references role (role_id),
    foreign key (permission_id) references permission (peremission_id)
);


/**
  添加用户,密码123,加密方式 BCryptPasswordEncoder
 */
insert into user (user_account, user_name, user_password)
values (1, 'admin', '$2a$10$XvP0pzuaDGg2t/ZFRxsX/uAgJeKVQigngKaTc5qIP8zX7D8xNyBja'),
       (2, 'user2', '$2a$10$XvP0pzuaDGg2t/ZFRxsX/uAgJeKVQigngKaTc5qIP8zX7D8xNyBja'),
       (3, 'user3', '$2a$10$XvP0pzuaDGg2t/ZFRxsX/uAgJeKVQigngKaTc5qIP8zX7D8xNyBja');

insert into role (role_id, role_name)
values (1, 'root'),
       (2, 'normal'),
       (3, 'tourist');

insert into permission (peremission_id, permission_name)
values (1, 'r'),
       (2, 'w'),
       (3, 'x');

insert into user_role (user_account, role_id)
values (1, 1),
       (2, 2),
       (3, 3);

insert into role_permission (role_id, permission_id)
values (1, 1),
       (1, 2),
       (1, 3),
       (2, 1),
       (2, 3),
       (3, 1);

-- ------------------------------------------------------------------------
select user.user_account, user_name, user_password, role_name, permission_name
from user
         join user_role on user.user_account = user_role.user_account
         join role on user_role.role_id = role.role_id
         join role_permission on role.role_id = role_permission.role_id
         join permission on role_permission.permission_id = permission.peremission_id;
drop view if exists user_role_permission;
-- 视图
/**
  用户密码,角色,权限
 */
create view user_role_permission as
select user.user_account, user_name, user_password, role.role_name, permission.permission_name
from user
         left join user_role on user.user_account = user_role.user_account
         left join role on user_role.role_id = role.role_id
         left join role_permission on role.role_id = role_permission.role_id
         left join permission on role_permission.permission_id = permission.peremission_id;

-- 视图显示
select *
from user_role_permission;
pom.xml
<?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>3.2.6</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.wangnan</groupId>
    <artifactId>spring_security</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>spring_security</name>
    <description>spring_security</description>
    <properties>
        <java.version>17</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>3.0.3</version>
        </dependency>
        <dependency>
            <groupId>org.thymeleaf.extras</groupId>
            <artifactId>thymeleaf-extras-springsecurity6</artifactId>
        </dependency>
        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter-test</artifactId>
            <version>3.0.3</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.alibaba.fastjson2</groupId>
            <artifactId>fastjson2</artifactId>
            <version>2.0.37</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>
配置
com.spring_security.config.SecurityConfig
定义密码编码器
@Bean("getpasswordEncoder")
public PasswordEncoder getpasswordEncoder() {
    return new BCryptPasswordEncoder();
}
配置过滤器
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    // 配置
   
    // 
    http.csrf(csrf -> csrf.disable());
    
    // 登出
    http.logout(logout -> logout
                .permitAll()
                .logoutSuccessHandler(new LogoutSuccessMessage()));
    
    return http.build();
}

权限控制

http.authorizeHttpRequests(
    authorize -> authorize
    // request 权限控制
    //                        .requestMatchers("/perm/read").hasAuthority("r")
    //                        .requestMatchers("/perm/write").hasAuthority("w")
    // 角色控制
    //                        .requestMatchers("/perm/write").hasRole("ROOT")
    .anyRequest()
    .authenticated()
);

登录认证 new的Handler是实现接口自定义的

http.formLogin(formLogin -> formLogin
               .loginPage("/dologin").permitAll() //登录页面无需授权即可访问
               .usernameParameter("username") //自定义表单用户名参数,默认是username
               .passwordParameter("password") //自定义表单密码参数,默认是password
               /* 登录失败的返回地址,使用
               .successHandler(new AuthenticationSuccessfulMessage())
               .failureHandler(new AuthenticationFailedMessage())
               会失效
               */
               // .failureUrl("/dologin?error") 
               .successHandler(new AuthenticationSuccessfulMessage())
               .failureHandler(new AuthenticationFailedMessage())
              );

认证授权异常 new的对象都是实现接口自定义的

http.exceptionHandling(
    exception -> exception
    // 未认证访问异常
    .authenticationEntryPoint(new AuthenticationEntryPointMessage())
    // 无权限异常
    .accessDeniedHandler(new AccessDeniedHandlerMessage())
);
DBUserDetailsManager
package com.spring_security.config;

import com.spring_security.entity.Permission;
import com.spring_security.entity.Role;
import com.spring_security.entity.User;
import com.spring_security.mapper.UserMapper;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsPasswordService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.provisioning.UserDetailsManager;
import org.springframework.stereotype.Component;

import java.util.Arrays;
import java.util.List;

/*****UTF-8*****
 * Description:
 * Author: 王 楠
 * Date: 2024/6/3 11:10
 * Proverbs: 吃的苦中苦,方为人上人
 */
@Component
public class DBUserDetailsManager implements UserDetailsManager, UserDetailsPasswordService {
    private final UserMapper userMapper;

    public DBUserDetailsManager(UserMapper userMapper) {
        this.userMapper = userMapper;
    }

    @Override
    public UserDetails updatePassword(UserDetails user, String newPassword) {
        return null;
    }

    @Override
    public void createUser(UserDetails user) {
    }

    @Override
    public void updateUser(UserDetails user) {
    }

    @Override
    public void deleteUser(String username) {
    }

    @Override
    public void changePassword(String oldPassword, String newPassword) {
    }

    @Override
    public boolean userExists(String username) {
        return false;
    }
    /*
    * 权限数组
    */

    public String[] getPermissionArr(User user) {
        List<Permission> permissions = user.getPermissions();
        String[] permissionArr = new String[permissions.size()];
        for (int i = 0; i < permissionArr.length; i++) {
            permissionArr[i] = permissions.get(i).getPermissionName();
        }
        return permissionArr;
    }

    /**
    角色数组
    */
    public String[] getRolesArr(User user) {
        List<Role> roles = user.getRoles();
        String[] roleArr = new String[roles.size()];
        for (int i = 0; i < roleArr.length; i++) {
            roleArr[i] = roles.get(i).getRoleName();
        }
        return roleArr;
    }


    /**
     * 在这个方法里标注用户的权限和角色信息,建立spring security框架里的用户对象,方法返回的就是用户画像
     *
     * @param username
     * @return
     * @throws UsernameNotFoundException
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
       return null;
    }
}

加载用户信息

UserDetails存放用户信息

在Spring Security中,UserDetails接口是一个核心接口,用于表示一个用户的基本信息,包括用户名、密码、权限等。这个接口主要由Spring Security的认证和授权机制使用,以确定用户是否有权执行特定的操作或访问特定的资源。

UserDetails接口定义了以下方法:

  • String getUsername(): 获取用户名。
  • String getPassword(): 获取密码。
  • Collection<? extends GrantedAuthority> getAuthorities(): 获取用户的权限列表。权限通常表示为GrantedAuthority对象,其中可能包括角色(如ROLE_USER)和其他特定的权限(如readwrite)。
  • boolean isAccountNonExpired(): 指示用户的账户是否未过期。
  • boolean isAccountNonLocked(): 指示用户的账户是否未锁定。
  • boolean isCredentialsNonExpired(): 指示用户的凭证(密码)是否未过期。
  • boolean isEnabled(): 指示用户是否启用。

UserDetails接口的实现类通常在应用程序中用于创建用户对象,这些对象随后由UserDetailsService在认证过程中加载。UserDetailsService是一个接口,用于根据用户名检索UserDetails对象。开发者通常需要实现这个接口,以便从数据库或其他数据源加载用户信息。

@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    // username前端表单传来的数据
    User user = userMapper.getUserByAccount(username);

    if (user != null) {
        // 权限数组
        String[] permissionArr = getPermissionArr(user);
        // 角色数组
        String[] rolesArr = getRolesArr(user);
        
        return null;
    } else {
        throw new UsernameNotFoundException(username);
    }
}
// GrantedAuthority 权限
Collection<GrantedAuthority> authorities = new ArrayList<>();

org.springframework.security.core.userdetails.User 是UserDetails实现类

return org.springframework.security.core.userdetails.User
    .withUsername(user.getUserAccount())
    .password(user.getUserPassword())
    /*
    roles(rolesArr)
    .authorities(permissionArr)
    这两个不能同时用,他的源码逻辑导致的
    */
    .roles(rolesArr)
    .authorities(permissionArr)
    .build();
登录
controller
@GetMapping("/dologin")
public String login(){
    return "login";
}
html
<!--
*****UTF-8*****
* Description:
* Author: 王 楠
* Date: 2024/5/27 20:40
* Proverbs: 吃的苦中苦,方为人上人
-->
<!DOCTYPE html>
<html xmlns:th="https://www.thymeleaf.org">
    <head>
        <title>登录</title>
        <meta charset="UTF-8">
    </head>
    <body>
        <h1>登录</h1>
        <div th:if="${param.error}">
            错误的用户名和密码.</div>

        <!--method必须为"post"-->
        <!--th:action="@{/login}" ,
使用动态参数,表单中会自动生成_csrf隐藏字段,用于防止csrf攻击
login: 和登录页面保持一致即可,SpringSecurity自动进行登录认证-->
        <form th:action="@{/dologin}" method="post">
            <div>
                <label for="username">username:</label>
                <!--name必须为"username"-->
                <input type="text" name="username" placeholder="用户名" id="username"/>
            </div>
            <div>
                <label for="password">password:</label>
                <!--name必须为"password"-->
                <input type="password" name="password" placeholder="密码" id="password"/>
            </div>
            <input type="submit" value="登录" />
        </form>
    </body>
</html>
配置
http.formLogin(formLogin -> formLogin
               .loginPage("/dologin").permitAll() //登录页面无需授权即可访问
               .usernameParameter("username") //自定义表单用户名参数,默认是username
               .passwordParameter("password") //自定义表单密码参数,默认是password           
               .failureUrl("/dologin?error") 
              );

这个登录有点类似于aop技术

Handler 处理

认证成功
package com.spring_security.message;

import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;

public class AuthenticationSuccessfulMessage implements AuthenticationSuccessHandler {
    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        // 获取用户身份信息
        Object principal = authentication.getPrincipal();
        // 建立返回结果
        //转换为json字符串
        // 返回响应
        response.setContentType("application/json;charset=UTF-8");
        response.getWriter().println(message);
    }
}
认证失败
package com.spring_security.message;

import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;


public class AuthenticationFailedMessage implements AuthenticationFailureHandler {

    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,AuthenticationException exception) throws IOException, ServletException {
        // 获取异常信息
        String localizedMessage = exception.getLocalizedMessage();
        // 建立返回结果
        //转换为json字符串
        // 返回响应
        response.setContentType("application/json;charset=UTF-8");
        response.getWriter().println(message);
    }
}
登出成功
package com.spring_security.message;

import com.alibaba.fastjson2.JSON;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;

public class LogoutSuccessMessage implements LogoutSuccessHandler {
    @Override
    public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        //创建结果对象
        //转换成json字符串
        //返回响应
        response.setContentType("application/json;charset=UTF-8");
        response.getWriter().println(message);
    }
}
权限不够
package com.spring_security.message;

import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;

public class AccessDeniedHandlerMessage implements AccessDeniedHandler {
    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
        //请求未授权的接口
        String exceptionMessage = accessDeniedException.getMessage();
        //创建结果对象
        //转换成json字符串
        //返回响应
        response.setContentType("application/json;charset=UTF-8");
        response.getWriter().println(message);
    }
}
认证未登录
package com.spring_security.message;

import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;

public class AuthenticationEntryPointMessage implements AuthenticationEntryPoint {
    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
        //创建结果对象
        //转换成json字符串
        //返回响应
        response.setContentType("application/json;charset=UTF-8");
        response.getWriter().println(json);
    }
}

用户认证信息

基本概念

securitycontextholder

在Spring Security框架中,SecurityContextHolder、SecurityContext、Authentication、Principal和Credential是一些与身份验证和授权相关的重要概念。它们之间的关系如下:

  1. SecurityContextHolder:SecurityContextHolder 是 Spring Security 存储已认证用户详细信息的地方。
  2. SecurityContext:SecurityContext 是从 SecurityContextHolder 获取的内容,包含当前已认证用户的 Authentication 信息。
  3. Authentication:Authentication 表示用户的身份认证信息。它包含了用户的Principal、Credential和Authority信息。
  4. Principal:表示用户的身份标识。它通常是一个表示用户的实体对象,例如用户名。Principal可以通过Authentication对象的getPrincipal()方法获取。
  5. Credentials:表示用户的凭证信息,例如密码、证书或其他认证凭据。Credential可以通过Authentication对象的getCredentials()方法获取。
  6. GrantedAuthority:表示用户被授予的权限

总结起来,SecurityContextHolder用于管理当前线程的安全上下文,存储已认证用户的详细信息,其中包含了SecurityContext对象,该对象包含了Authentication对象,后者表示用户的身份验证信息,包括Principal(用户的身份标识)和Credential(用户的凭证信息)。

在Controller中获取用户信息
@GetMapping("/userInfo")
@ResponseBody
public Map<String, Object> userInfo() {
    
    SecurityContext context = SecurityContextHolder.getContext();
    
    Authentication authentication = context.getAuthentication();
    
    String username = authentication.getName();//用户名
    org.springframework.security.core.userdetails.User user = (org.springframework.security.core.userdetails.User) authentication.getPrincipal();//身份
    Object credentials = authentication.getCredentials();//凭证(脱敏)

    Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();//权限
    // 将权限列表转换为字符串列表
    List<String> authorities_ = authorities.stream()
        .map(GrantedAuthority::getAuthority)
        .collect(Collectors.toList());

    Collection<GrantedAuthority> authorities1 = user.getAuthorities();
    // 将权限列表转换为字符串列表
    List<String> authorities1_ = authorities1.stream()
        .map(GrantedAuthority::getAuthority)
        .collect(Collectors.toList());

    Map<String, Object> message = new LinkedHashMap<>();
    message.put("userName", username);
    message.put("principal", user);
    message.put("credentials", credentials);
    message.put("authorities_", authorities_);
    message.put("authorities1_", authorities1_);

    return message;
}

权限认证

授权

授权管理的实现在SpringSecurity中非常灵活,可以帮助应用程序实现以下两种常见的授权需求:

  • 用户-权限-资源:例如张三的权限是添加用户、查看用户列表,李四的权限是查看用户列表

  • 用户-角色-权限-资源:例如 张三是角色是管理员、李四的角色是普通用户,管理员能做所有操作,普通用户只能查看信息

基于方法授权
package com.spring_security.controller;

import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;

/*****UTF-8*****
 * Description:
 * Author: 王 楠
 * Date: 2024/6/6 13:11
 * Proverbs: 吃的苦中苦,方为人上人
 */
@Controller
@RequestMapping("/perm")
// 开启权限方法注解
@EnableMethodSecurity
public class PermController {

    // 权限授权 hasAuthority('r')
    @PreAuthorize("hasAuthority('r')")
    @GetMapping("/read")
    public String read() {
        return "read";
    }

    // 角色授权 hasRole('root')
    @PreAuthorize("hasRole('root')")
    //    @PreAuthorize("hasAuthority('w')")
    @GetMapping("/write")
    public String write() {
        return "write";
    }

    @PreAuthorize("hasAuthority('x')")
    @GetMapping("/execute")
    public String execute() {
        return "execute";
    }
}

在配置文件中添加如下注解

// 开启权限方法注解
@EnableMethodSecurity
// 角色授权 hasRole('root')
@PreAuthorize("hasRole('root')")
// 权限授权 hasAuthority('r')
@PreAuthorize("hasAuthority('r')")
基于request的授权
用户-权限-资源

需求:

  • 具有USER_LIST权限的用户可以访问/user/list接口
  • 具有USER_ADD权限的用户可以访问/user/add接口
配置权限

SecurityFilterChain

//开启授权保护
http.authorizeRequests(
        authorize -> authorize
    			//具有USER_LIST权限的用户可以访问/user/list
                .requestMatchers("/user/list").hasAuthority("USER_LIST")
    			//具有USER_ADD权限的用户可以访问/user/add
    			.requestMatchers("/user/add").hasAuthority("USER_ADD")
                //对所有请求开启授权保护
                .anyRequest()
                //已认证的请求会被自动授权
                .authenticated()
        );
授予权限

DBUserDetailsManager中的loadUserByUsername方法:

Collection<GrantedAuthority> authorities = new ArrayList<>();
authorities.add(()->"USER_LIST");
authorities.add(()->"USER_ADD");

/*authorities.add(new GrantedAuthority() {
    @Override
    public String getAuthority() {
        return "USER_LIST";
    }
});
authorities.add(new GrantedAuthority() {
    @Override
    public String getAuthority() {
        return "USER_ADD";
    }
});*/

更多的例子:Authorize HttpServletRequests :: Spring Security

用户-角色-资源

**需求:**角色为ADMIN的用户才可以访问/user/**路径下的资源

配置角色

SecurityFilterChain

//开启授权保护
http.authorizeRequests(
        authorize -> authorize
                //具有管理员角色的用户可以访问/user/**
                .requestMatchers("/user/**").hasRole("ADMIN")
                //对所有请求开启授权保护
                .anyRequest()
                //已认证的请求会被自动授权
                .authenticated()
);
授予角色

DBUserDetailsManager中的loadUserByUsername方法:

return org.springframework.security.core.userdetails.User
        .withUsername(user.getUsername())
        .password(user.getPassword())
        .roles("ADMIN")
        .build();
用户-角色-权限-资源

RBAC(Role-Based Access Control,基于角色的访问控制)是一种常用的数据库设计方案,它将用户的权限分配和管理与角色相关联。以下是一个基本的RBAC数据库设计方案的示例:

  1. 用户表(User table):包含用户的基本信息,例如用户名、密码和其他身份验证信息。
列名数据类型描述
user_idint用户ID
usernamevarchar用户名
passwordvarchar密码
emailvarchar电子邮件地址
  1. 角色表(Role table):存储所有可能的角色及其描述。
列名数据类型描述
role_idint角色ID
role_namevarchar角色名称
descriptionvarchar角色描述
  1. 权限表(Permission table):定义系统中所有可能的权限。
列名数据类型描述
permission_idint权限ID
permission_namevarchar权限名称
descriptionvarchar权限描述
  1. 用户角色关联表(User-Role table):将用户与角色关联起来。
列名数据类型描述
user_role_idint用户角色关联ID
user_idint用户ID
role_idint角色ID
  1. 角色权限关联表(Role-Permission table):将角色与权限关联起来。
列名数据类型描述
role_permission_idint角色权限关联ID
role_idint角色ID
permission_idint权限ID

在这个设计方案中,用户可以被分配一个或多个角色,而每个角色又可以具有一个或多个权限。通过对用户角色关联和角色权限关联表进行操作,可以实现灵活的权限管理和访问控制。

当用户尝试访问系统资源时,系统可以根据用户的角色和权限决定是否允许访问。这样的设计方案使得权限管理更加简单和可维护,因为只需调整角色和权限的分配即可,而不需要针对每个用户进行单独的设置。

代码地址 https://gitee.com/nan-mu-wang/spring

. | … | … |

  1. 用户角色关联表(User-Role table):将用户与角色关联起来。
列名数据类型描述
user_role_idint用户角色关联ID
user_idint用户ID
role_idint角色ID
  1. 角色权限关联表(Role-Permission table):将角色与权限关联起来。
列名数据类型描述
role_permission_idint角色权限关联ID
role_idint角色ID
permission_idint权限ID

在这个设计方案中,用户可以被分配一个或多个角色,而每个角色又可以具有一个或多个权限。通过对用户角色关联和角色权限关联表进行操作,可以实现灵活的权限管理和访问控制。

当用户尝试访问系统资源时,系统可以根据用户的角色和权限决定是否允许访问。这样的设计方案使得权限管理更加简单和可维护,因为只需调整角色和权限的分配即可,而不需要针对每个用户进行单独的设置。

代码地址 https://gitee.com/nan-mu-wang/spring
  • 28
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值