在后端开发领域认证Authentication和授权Authorization是系统安全的核心基石。简单来说认证是确认「你是谁」授权是决定「你能做什么」。Spring Security 作为 Spring 生态官方推荐的安全框架凭借高度模块化、可扩展、无缝整合 SpringBoot 等特性成为 Java 后端安全开发的首选方案。一、前置知识与环境说明1.1 核心概念1.认证Authentication验证用户身份是否合法比如输入账号密码登录、短信验证码登录都属于认证流程。2.授权Authorization认证通过后根据用户的角色/权限控制用户能访问哪些接口、页面比如管理员能访问所有接口普通用户只能访问个人接口。3.Spring Security基于 Spring AOP 和 Servlet 过滤器实现的安全框架默认提供了表单登录、注销、会话管理、csrf 防护、权限控制等全套安全能力。1.2 开发环境• JDK 8• SpringBoot 2.7.x稳定版兼容绝大多数企业项目• Maven 3.6• IDEA 开发工具• Lombok简化代码二、项目初始化与依赖引入2.1 创建 SpringBoot 项目打开 IDEA创建一个标准的 SpringBoot 项目项目名称springboot-security-demo。2.2 核心依赖pom.xml直接复制以下依赖无需额外版本号SpringBoot 自动管理版本兼容?xml version1.0 encodingUTF-8? project xmlnshttp://maven.apache.org/POM/4.0.0 xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd modelVersion4.0.0/modelVersion parent groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-parent/artifactId version2.7.15/version relativePath/ /parent groupIdcom.example/groupId artifactIdspringboot-security-demo/artifactId version0.0.1-SNAPSHOT/version namespringboot-security-demo/name properties java.version1.8/java.version /properties dependencies !-- SpringBoot Web 核心依赖 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency !-- Spring Security 安全框架 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-security/artifactId /dependency !-- Lombok 简化代码 -- dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId optionaltrue/optional /dependency !-- 测试依赖 -- dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-test/artifactId scopetest/scope /dependency /dependencies build plugins plugin groupIdorg.springframework.boot/groupId artifactIdspring-boot-maven-plugin/artifactId configuration excludes exclude groupIdorg.projectlombok/groupId artifactIdlombok/artifactId /exclude /excludes /configuration /plugin /plugins /build /project2.3 启动项目体验默认安全机制直接启动 SpringBoot 项目控制台会打印一段默认密码Using generated security password: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx默认用户名user默认密码控制台打印的随机字符串访问测试接口http://localhost:8080会自动跳转到 Spring Security 提供的默认登录页面输入账号密码即可登录成功。这就是 Spring Security 的自动配置机制零代码实现基础登录保护但实际项目中我们必须自定义登录逻辑、用户信息、权限规则。三、Spring Security 核心架构理解在动手写代码前先搞懂核心组件后续配置会一目了然1.SecurityContextHolder安全上下文持有者存储当前登录用户的信息Authentication 对象。2.Authentication认证对象包含用户身份信息、权限信息、认证状态。3.UserDetailsService核心接口用于加载用户信息我们必须实现它自定义查询用户逻辑。4.PasswordEncoder密码加密器Spring Security 强制要求密码加密不能明文存储。5.SecurityFilterChain安全过滤器链所有认证、授权、防护逻辑都通过过滤器链执行。6.PreAuthorize权限注解用于控制方法/接口的访问权限。四、内存用户认证第一步我们先使用内存用户实现登录认证不连接数据库快速理解配置逻辑。4.1 创建 Security 配置类创建配置类com.example.config.SecurityConfigpackage com.example.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.provisioning.InMemoryUserDetailsManager; /** * Spring Security 核心配置类 * EnableWebSecurity 开启 Spring Security web 安全支持 */ Configuration EnableWebSecurity public class SecurityConfig { /** * 密码加密器 * 推荐使用 BCrypt 算法自动加盐不可逆加密 */ Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } /** * 内存用户信息管理 * 定义两个用户admin管理员、user普通用户 */ Bean public UserDetailsService userDetailsService() { // 管理员用户 UserDetails admin User.builder() .username(admin) .password(passwordEncoder().encode(123456)) // 密码加密 .roles(ADMIN) // 角色 .build(); // 普通用户 UserDetails user User.builder() .username(user) .password(passwordEncoder().encode(123456)) .roles(USER) .build(); // 内存存储用户信息 return new InMemoryUserDetailsManager(admin, user); } }4.2 配置说明1.EnableWebSecurity开启 Spring Security 安全配置。2.PasswordEncoder必须配置Spring Security 禁止明文密码BCrypt 是官方推荐加密方式。3.UserDetailsService用户信息加载接口这里使用内存实现。4..roles()为用户分配角色用于后续授权控制。4.3 测试登录重启项目访问http://localhost:8080/login• 管理员账号admin / 123456• 普通用户账号user / 123456登录成功后即可访问所有接口此时还未做权限控制。五、URL 权限控制我们需要对不同接口设置不同的访问权限比如• 公共接口所有人可访问登录、首页、静态资源• 用户接口仅登录用户可访问• 管理员接口仅 ADMIN 角色可访问5.1 修改 SecurityConfig添加 HttpSecurity 配置在SecurityConfig类中添加以下方法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.web.SecurityFilterChain; /** * SpringBoot 2.7 推荐使用 Lambda 风格配置 * 弃用 WebSecurityConfigurerAdapter */ Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http // 1. 授权配置 .authorizeRequests() // 公共接口无需登录即可访问 .antMatchers(/, /home, /login, /css/**, /js/**).permitAll() // 管理员接口仅 ADMIN 角色可访问 .antMatchers(/admin/**).hasRole(ADMIN) // 用户接口仅 USER 角色可访问 .antMatchers(/user/**).hasRole(USER) // 所有其他请求必须登录才能访问 .anyRequest().authenticated() .and() // 2. 表单登录配置 .formLogin() // 自定义登录页面可选 .loginPage(/login) // 登录请求处理接口 .loginProcessingUrl(/doLogin) // 登录成功默认跳转页面 .defaultSuccessUrl(/home) // 登录失败跳转页面 .failureUrl(/login?errortrue) // 允许所有人访问登录相关接口 .permitAll() .and() // 3. 登出配置 .logout() // 登出请求接口 .logoutUrl(/logout) // 登出成功跳转页面 .logoutSuccessUrl(/login?logouttrue) // 登出后清除会话 .invalidateHttpSession(true) // 清除认证信息 .clearAuthentication(true) .permitAll() .and() // 4. 关闭 CSRF 防护前后端分离项目建议关闭 .csrf().disable(); return http.build(); }5.2 权限规则说明1.permitAll()所有人可访问无需登录。2.hasRole(角色名)必须拥有指定角色才能访问。3.anyRequest().authenticated()除了上面配置的接口其余都需要登录。4.formLogin()开启表单登录Spring Security 自动生成登录接口。5.logout()开启登出功能自动清理会话。5.3 创建测试接口创建控制器com.example.controller.TestControllerpackage com.example.controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; RestController public class TestController { // 公共接口 GetMapping(/home) public String home() { return 首页所有人可访问; } // 普通用户接口 GetMapping(/user/info) public String userInfo() { return 用户信息仅 USER 角色可访问; } // 管理员接口 GetMapping(/admin/info) public String adminInfo() { return 管理员信息仅 ADMIN 角色可访问; } // 测试接口 GetMapping(/test) public String test() { return 测试接口仅登录用户可访问; } }5.4 权限测试1. 未登录访问/admin/info、/user/info、/test都会自动跳转到登录页。2. 登录user用户• 可访问/home、/user/info、/test• 不可访问/admin/info403 无权限3. 登录admin用户• 可访问所有接口。完美实现基于角色的 URL 权限控制六、注解式权限控制除了 URL 配置Spring Security 还支持方法级注解权限更灵活适合分布式、微服务项目。6.1 开启注解权限在启动类添加EnableMethodSecuritySpringBoot 2.7或EnableGlobalMethodSecurity旧版package com.example; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; // 开启方法级权限控制 EnableMethodSecurity(prePostEnabled true) SpringBootApplication public class SpringbootSecurityDemoApplication { public static void main(String[] args) { SpringApplication.run(SpringbootSecurityDemoApplication.class, args); } }6.2 常用权限注解1.PreAuthorize方法执行前校验权限最常用2.PostAuthorize方法执行后校验权限3.Secured旧版角色注解6.3 实战使用RestController public class AuthController { // 仅 ADMIN 角色可访问 PreAuthorize(hasRole(ADMIN)) GetMapping(/auth/admin) public String authAdmin() { return 注解权限管理员专属接口; } // 仅 USER 角色可访问 PreAuthorize(hasRole(USER)) GetMapping(/auth/user) public String authUser() { return 注解权限普通用户专属接口; } // 拥有 ADMIN 或 USER 角色均可访问 PreAuthorize(hasAnyRole(ADMIN,USER)) GetMapping(/auth/common) public String authCommon() { return 注解权限登录用户均可访问; } // 拥有指定权限标识非角色 PreAuthorize(hasAuthority(user:add)) GetMapping(/auth/authority) public String authAuthority() { return 注解权限拥有 user:add 权限可访问; } }注解式权限代码侵入性低、灵活度高是企业项目主流用法。七、数据库查询用户认证内存用户仅适合测试真实项目必须从数据库加载用户。核心步骤1. 实现UserDetailsService接口2. 重写loadUserByUsername方法3. 从数据库查询用户信息角色权限4. 封装成UserDetails对象返回7.1 引入数据库依赖可选 MyBatis/MyBatis-Plus/JPA这里以 MyBatis-Plus 为例简化数据库操作!-- MySQL 驱动 -- dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId scoperuntime/scope /dependency !-- MyBatis-Plus -- dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version3.5.3.1/version /dependency7.2 数据库表设计-- 用户表 CREATE TABLE sys_user ( id bigint NOT NULL AUTO_INCREMENT, username varchar(50) NOT NULL COMMENT 用户名, password varchar(100) NOT NULL COMMENT 密码BCrypt加密, status int DEFAULT 1 COMMENT 状态 0-禁用 1-正常, PRIMARY KEY (id), UNIQUE KEY username (username) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 角色表 CREATE TABLE sys_role ( id bigint NOT NULL AUTO_INCREMENT, role_name varchar(50) NOT NULL COMMENT 角色名称, role_code varchar(50) NOT NULL COMMENT 角色编码 ADMIN/USER, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 用户角色关联表 CREATE TABLE sys_user_role ( user_id bigint NOT NULL, role_id bigint NOT NULL, PRIMARY KEY (user_id,role_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 插入测试数据 INSERT INTO sys_user (username, password, status) VALUES (admin, $2a$10$wJ5i4y9y9Q9m0pOe1x2L3u4S5D6F7G8H9J0K1L2M3N4B5V6C7X8Z9A, 1), (user, $2a$10$wJ5i4y9y9Q9m0pOe1x2L3u4S5D6F7G8H9J0K1L2M3N4B5V6C7X8Z9A, 1); INSERT INTO sys_role (role_name, role_code) VALUES (管理员, ADMIN),(普通用户, USER); INSERT INTO sys_user_role (user_id, role_id) VALUES (1,1),(2,2);密码明文123456已使用 BCrypt 加密。7.3 自定义 UserDetails 实现类Spring Security 默认的 User 类不够用我们自定义package com.example.entity; import lombok.Data; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; import java.util.Collection; import java.util.List; import java.util.stream.Collectors; /** * 自定义用户详情实现 UserDetails 接口 */ Data public class SecurityUser implements UserDetails { // 用户ID private Long id; // 用户名 private String username; // 密码 private String password; // 状态 private Integer status; // 角色列表 private ListString roles; /** * 获取权限/角色集合 * Spring Security 要求角色必须以 ROLE_ 开头 */ Override public Collection? extends GrantedAuthority getAuthorities() { return roles.stream() .map(role - new SimpleGrantedAuthority(ROLE_ role)) .collect(Collectors.toList()); } Override public String getPassword() { return this.password; } Override public String getUsername() { return this.username; } // 账户是否未过期 Override public boolean isAccountNonExpired() { return true; } // 账户是否未锁定 Override public boolean isAccountNonLocked() { return true; } // 密码是否未过期 Override public boolean isCredentialsNonExpired() { return true; } // 账户是否可用 Override public boolean isEnabled() { return this.status 1; } }7.4 实现 UserDetailsService 核心接口package com.example.service.impl; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.example.entity.SecurityUser; import com.example.entity.SysUser; import com.example.service.SysRoleService; import com.example.service.SysUserService; import org.springframework.beans.factory.annotation.Autowired; 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; /** * 自定义用户认证服务 * 核心从数据库查询用户信息 */ Service public class UserDetailsServiceImpl implements UserDetailsService { Autowired private SysUserService userService; Autowired private SysRoleService roleService; /** * 根据用户名加载用户信息 */ Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { // 1. 根据用户名查询用户 SysUser user userService.getOne(Wrappers.SysUserlambdaQuery() .eq(SysUser::getUsername, username)); if (user null) { throw new UsernameNotFoundException(用户名不存在); } // 2. 查询用户角色 ListString roles roleService.getRolesByUserId(user.getId()); // 3. 封装成 SecurityUser 对象返回 SecurityUser securityUser new SecurityUser(); securityUser.setId(user.getId()); securityUser.setUsername(user.getUsername()); securityUser.setPassword(user.getPassword()); securityUser.setStatus(user.getStatus()); securityUser.setRoles(roles); return securityUser; } }7.5 配置类注入自定义 UserDetailsService删除之前的内存用户配置直接使用我们自定义的服务Configuration EnableWebSecurity public class SecurityConfig { Autowired private UserDetailsService userDetailsService; Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .userDetailsService(userDetailsService) // 注入自定义用户服务 .authorizeRequests() .antMatchers(/, /home, /login).permitAll() .antMatchers(/admin/**).hasRole(ADMIN) .antMatchers(/user/**).hasRole(USER) .anyRequest().authenticated() .and() .formLogin() .loginPage(/login) .loginProcessingUrl(/doLogin) .defaultSuccessUrl(/home) .permitAll() .and() .logout() .logoutUrl(/logout) .logoutSuccessUrl(/login) .permitAll() .and() .csrf().disable(); return http.build(); } }✅至此企业级数据库认证配置完成登录逻辑完全基于数据库查询支持多角色、账户状态控制。八、登录用户信息获取在业务代码中我们经常需要获取当前登录用户信息Spring Security 提供了便捷工具import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; RestController public class UserInfoController { /** * 获取当前登录用户信息 */ GetMapping(/getUserInfo) public String getUserInfo() { // 1. 获取认证对象 Authentication authentication SecurityContextHolder.getContext().getAuthentication(); // 2. 判断是否登录 if (authentication null || !authentication.isAuthenticated()) { return 未登录; } // 3. 获取用户信息 Object principal authentication.getPrincipal(); if (principal instanceof UserDetails) { SecurityUser securityUser (SecurityUser) principal; return 当前登录用户 securityUser.getUsername() 角色 securityUser.getRoles(); } return 获取用户信息失败; } }九、异常处理401 未认证 / 403 无权限Spring Security 默认异常页面不友好我们可以自定义异常处理package com.example.config; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.web.AuthenticationEntryPoint; import org.springframework.security.web.access.AccessDeniedHandler; import javax.servlet.http.HttpServletResponse; import java.util.HashMap; import java.util.Map; /** * 自定义安全异常处理 */ Configuration public class SecurityExceptionConfig { /** * 401 未登录处理 */ Bean public AuthenticationEntryPoint authenticationEntryPoint() { return (request, response, authException) - { response.setContentType(application/json;charsetutf-8); response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); MapString, Object result new HashMap(); result.put(code, 401); result.put(msg, 未登录请先登录); new ObjectMapper().writeValue(response.getWriter(), result); }; } /** * 403 无权限处理 */ Bean public AccessDeniedHandler accessDeniedHandler() { return (request, response, accessDeniedException) - { response.setContentType(application/json;charsetutf-8); response.setStatus(HttpServletResponse.SC_FORBIDDEN); MapString, Object result new HashMap(); result.put(code, 403); result.put(msg, 无权限访问该资源); new ObjectMapper().writeValue(response.getWriter(), result); }; } }在 SecurityConfig 中注入http.exceptionHandling() .authenticationEntryPoint(authenticationEntryPoint) .accessDeniedHandler(accessDeniedHandler);十、Spring Security 核心知识点总结1.两大核心认证登录 授权权限2.核心接口UserDetailsService加载用户、PasswordEncoder密码加密3.两种权限控制URL 配置 方法注解4.密码必须加密BCrypt 算法是最佳实践5.前后端分离关闭 CSRF使用 Token 认证下篇文章讲解6.异常统一处理401 未登录、403 无权限结语Spring Security 是 Java 后端必备安全框架掌握基础认证与授权足以应对绝大多数企业项目的安全需求。如果文章对你有帮助欢迎点赞、在看、转发你的支持是我持续更新的动力有任何问题欢迎在评论区留言交流~