SpringSecurity
SpringSecurity


认证(Authentication)
pom.xml
在pom.xml中引入springboot及springsecurity
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.0</version>
</parent>
<dependencies>
<!-- springsecurity -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
application.yaml配置文件
spring:
# Spring Security 配置项,对应 SecurityProperties 配置类
security:
# 配置默认的 InMemoryUserDetailsManager 的用户账号与密码。
user:
name: admin # 账号
password: 123456 # 密码
roles: ADMIN # 拥有角色
代码
package com.autumn.springsecurity.controller;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.security.PermitAll;
@RestController
@RequestMapping("/hellocontroller")
public class HelloController {
@RequestMapping("/hello")
public String hello(){
return "hello";
}
@GetMapping("/home")
public String home() {
return "首页";
}
@GetMapping("/admin")
public String admin() {
return "管理员";
}
@GetMapping("/normal")
public String normal() {
return "普通用户";
}
}
启动springboot项目,http://localhost:8080登录界面输入admin 123456认证后才可访问http://localhost:8080/hellocontroller/hello等一系列接口

鉴权配置SecurityConfig
package com.autumn.springsecurity.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
@Configuration
//允许使用注解方式配置
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
/**
* 授权逻辑,控制了“什么角色能访问什么资源”
* @param http
* @throws Exception
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
//授权配置
http
// 配置请求地址的权限
.authorizeRequests()
.antMatchers("/hellocontroller/home").permitAll() // 所有用户可访问
.antMatchers("/hellocontroller/admin").hasRole("ADMIN") // 需要 ADMIN 角色
.antMatchers("/hellocontroller/normal").access("hasRole('ROLE_NORMAL')") // 需要 NORMAL 角色。
// 任何请求,访问的用户都需要经过认证
.anyRequest().authenticated()
.and()
// 设置 Form 表单登陆
.formLogin()
// .loginPage("/login") // 登陆 URL 地址
.permitAll() // 所有用户可访问
.and()
// 配置退出相关
.logout()
// .logoutUrl("/logout") // 退出 URL 地址
.permitAll(); // 所有用户可访问
}
/**
* 认证逻辑
* 配置用户名密码及角色
* @param auth
* @throws Exception
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.
// 使用内存中的 InMemoryUserDetailsManager
inMemoryAuthentication()
// 不使用 PasswordEncoder 密码编码器
.passwordEncoder(NoOpPasswordEncoder.getInstance())
// 配置 admin 用户,会覆盖application.yaml 中的配置的admin账号
.withUser("admin").password("111").roles("ADMIN")
// 配置 normal 用户
.and().withUser("user").password("111").roles("NORMAL");
}
}
登录user账号

访问admin接口,会显示403(因为admin接口需要admin角色,而user是normal角色)

注解方式
添加@EnableGlobalMethodSecurity(prePostEnabled = true)
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter
Controller使用权限注解
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.security.PermitAll;
@RestController
@RequestMapping("/ss")
public class SSController {
@PermitAll
@GetMapping("/hello")
public String hello() {
return "hello";
}
@GetMapping("/home")
public String home() {
return "首页";
}
@PreAuthorize("hasRole('ROLE_ADMIN')")
@GetMapping("/admin")
public String admin() {
return "管理员";
}
@PreAuthorize("hasRole('ROLE_NORMAL')")
@GetMapping("/normal")
public String normal() {
return "普通用户";
}
}
登录admin账户

可以访问认证接口和拥有角色接口

权限控制
| 方法名 | 说明 | 适用场景 | 常用程度 |
|---|---|---|---|
permitAll() |
所有用户都可以访问 | 公共资源,如登录页、首页、静态资源 | ⭐⭐⭐⭐ |
denyAll() |
所有用户都不可以访问 | 屏蔽某些接口 | ⭐ |
authenticated() |
只允许已登录用户访问 | 需要身份认证的接口 | ⭐⭐⭐⭐ |
anonymous() |
允许匿名用户访问(未登录即可访问) | 注册页、找回密码页 | ⭐⭐ |
rememberMe() |
通过 “记住我” 登录的用户可访问 | 提供记住登录功能的场景 | ⭐⭐ |
fullyAuthenticated() |
必须是完全登录(非 remember-me)用户 | 敏感操作,如修改密码 | ⭐⭐ |
hasIpAddress(String ip) |
限制特定 IP 的用户访问 | 接口白名单、公司内网 | ⭐⭐ |
hasRole(String role) |
拥有指定角色的用户可访问 | RBAC 基于角色的访问控制 | ⭐⭐⭐⭐⭐ |
hasAnyRole(String... roles) |
拥有任意一个角色即可访问 | 多角色支持,如 ADMIN 或 USER |
⭐⭐⭐⭐⭐ |
hasAuthority(String authority) |
拥有指定权限的用户可访问 | RBAC 基于权限的访问控制 | ⭐⭐⭐⭐⭐ |
hasAnyAuthority(String... authorities) |
拥有任意一个权限即可访问 | 多权限支持,如 READ 或 WRITE |
⭐⭐⭐⭐⭐ |
access(String expression) |
使用 SpEL 表达式实现更复杂的权限控制 | 复杂业务逻辑,如根据用户属性动态判断 | ⭐⭐⭐ |
not().hasRole(String role) |
没有某个角色的用户可访问 | 限制管理员访问普通接口 | ⭐ |
isRememberMe() |
判断用户是否通过 RememberMe 登录 | 个性化权限 | ⭐ |
isAnonymous() |
判断用户是否匿名 | 区分登录与未登录状态 | ⭐ |
isAuthenticated() |
判断用户是否已登录 | 常用于表达式中 | ⭐⭐⭐ |
principal |
获取当前登录用户对象 | 在 SpEL 中动态获取用户信息 | ⭐⭐⭐ |
hasPermission(Object target, Object permission) |
基于 ACL 的权限控制 | 精细化到数据级别的安全控制 | ⭐⭐ |
使用数据库动态配置认证和授权(未经测试)
SpringBoot2.5版本,使用继承WebSecurityConfigurerAdapter方法
数据库
-- 用户表
CREATE TABLE users (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(64) NOT NULL UNIQUE,
password VARCHAR(255) NOT NULL,
enabled TINYINT DEFAULT 1
);
-- 角色表
CREATE TABLE roles (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
code VARCHAR(64) NOT NULL UNIQUE COMMENT 'ROLE_ADMIN / ROLE_USER',
name VARCHAR(64) NOT NULL
);
-- 用户-角色关系表
CREATE TABLE user_roles (
user_id BIGINT NOT NULL,
role_id BIGINT NOT NULL,
PRIMARY KEY(user_id, role_id)
);
-- 权限表(接口)
CREATE TABLE permissions (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
url VARCHAR(255) NOT NULL,
method VARCHAR(10) DEFAULT 'GET',
description VARCHAR(128)
);
-- 角色-权限关系表
CREATE TABLE role_permissions (
role_id BIGINT NOT NULL,
permission_id BIGINT NOT NULL,
PRIMARY KEY(role_id, permission_id)
);
SecurityConfig(Spring Boot 2.5.0)
package com.autumn.springsecurity.config;
import com.autumn.springsecurity.service.CustomUserDetailsService;
import com.autumn.springsecurity.service.DynamicSecurityService;
import lombok.RequiredArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
//用户和角色绑定
private final CustomUserDetailsService userDetailsService;
//角色和菜单绑定
private final DynamicSecurityService dynamicSecurityService;
/**
* 密码加密器
*/
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
/**
* 配置认证方式
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
provider.setUserDetailsService(userDetailsService);
provider.setPasswordEncoder(passwordEncoder());
auth.authenticationProvider(provider);
}
/**
* 配置 URL 权限规则
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable() // 开发阶段关闭 CSRF
.authorizeRequests()
// 1. 登录、注册、错误页面直接放行
.antMatchers("/login", "/register", "/error").permitAll()
// 2. 动态加载数据库中的 URL -> 角色映射
.antMatchers(dynamicSecurityService.loadUrlRoleMappings().keySet()
.toArray(new String[0]))
.access("@dynamicSecurityService.hasPermission(request, authentication)")
// 3. 其他请求需要登录
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login") // 自定义登录页
.loginProcessingUrl("/doLogin") // 登录接口
.defaultSuccessUrl("/index", true) // 登录成功跳转
.failureUrl("/login?error")
.permitAll()
.and()
.logout()
.logoutUrl("/logout")
.logoutSuccessUrl("/login")
.permitAll();
}
}
CustomUserDetailsService认证
package com.autumn.springsecurity.service;
import com.autumn.springsecurity.entity.User;
import com.autumn.springsecurity.mapper.UserMapper;
import lombok.RequiredArgsConstructor;
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.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.stream.Collectors;
@Service
@RequiredArgsConstructor
public class CustomUserDetailsService implements UserDetailsService {
private final UserMapper userMapper;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// 1. 查询用户
User user = userMapper.findByUsername(username);
if (user == null) {
throw new UsernameNotFoundException("用户不存在");
}
// 2. 查询角色
List<String> roles = userMapper.findRolesByUserId(user.getId());
// 3. 转换为 Spring Security 权限对象
List<GrantedAuthority> authorities = roles.stream()
.map(SimpleGrantedAuthority::new)
.collect(Collectors.toList());
return org.springframework.security.core.userdetails.User.builder()
.username(user.getUsername())
.password(user.getPassword())
.authorities(authorities)
.disabled(!user.getEnabled())
.build();
}
}
DynamicSecurityService(动态权限加载)
package com.autumn.springsecurity.service;
import com.autumn.springsecurity.mapper.PermissionMapper;
import com.autumn.springsecurity.dto.PermissionRoleDTO;
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.stream.Collectors;
@Service
@RequiredArgsConstructor
public class DynamicSecurityService {
private final PermissionMapper permissionMapper;
/**
* 加载 URL -> 角色 映射
*/
public Map<String, String[]> loadUrlRoleMappings() {
List<PermissionRoleDTO> list = permissionMapper.findAllUrlRoleMappings();
return list.stream().collect(Collectors.groupingBy(
PermissionRoleDTO::getUrl,
Collectors.mapping(PermissionRoleDTO::getRoleCode, Collectors.toList())
)).entrySet().stream()
.collect(Collectors.toMap(Map.Entry::getKey, e -> e.getValue().toArray(new String[0])));
}
/**
* 判断当前用户是否有访问权限
*/
public boolean hasPermission(HttpServletRequest request, Authentication authentication) {
Map<String, String[]> urlRoleMappings = loadUrlRoleMappings();
String requestURI = request.getRequestURI();
if (!urlRoleMappings.containsKey(requestURI)) {
return true; // 未配置的接口默认放行
}
String[] roles = urlRoleMappings.get(requestURI);
return authentication.getAuthorities().stream()
.anyMatch(a -> Arrays.asList(roles).contains(a.getAuthority()));
}
}
Mapper 层
@Mapper
public interface PermissionMapper {
@Select("""
SELECT p.url, r.code AS roleCode
FROM permissions p
JOIN role_permissions rp ON p.id = rp.permission_id
JOIN roles r ON r.id = rp.role_id
""")
List<PermissionRoleDTO> findAllUrlRoleMappings();
}
如果这篇文章对你有用,可以关注本人微信公众号获取更多ヽ(^ω^)ノ ~


浙公网安备 33010602011771号