欢迎光临
我们一直在努力

Spring Security 从入门到实战:搞定认证授权,再也不用手写权限逻辑

在这里插入图片描述

✨道路是曲折的,前途是光明的!

📝 专注C/C++、Linux编程与人工智能领域,分享学习笔记!

🌟 感谢各位小伙伴的长期陪伴与支持,欢迎文末添加好友一起交流!

在这里插入图片描述

  • 目录
    • 前言
    • 一、Spring Security 是什么?
      • 1.1 核心功能
    • 二、技术组成分布
    • 三、认证流程解析
      • 3.1 请求处理流程
      • 3.2 过滤器链结构
    • 四、快速开始
      • 4.1 添加依赖
      • 4.2 基础配置
    • 五、认证机制详解
      • 5.1 内存认证(开发测试)
      • 5.2 数据库认证(生产推荐)
    • 六、授权控制方式
      • 6.1 注解方式
      • 6.2 配置方式
    • 七、JWT 无状态认证
      • 7.1 JWT 认证流程
      • 7.2 JWT 实现
    • 八、常见场景解决方案
      • 8.1 场景一:记住我功能
      • 8.2 场景二:退出登录
      • 8.3 场景三:CSRF 防护
      • 8.4 场景四:自定义登录页
    • 九、学习路线
      • 9.1 学习时间建议
    • 十、最佳实践
      • 10.1 密码加密
      • 10.2 权限设计原则
      • 10.3 异常处理
      • 10.4 安全配置
    • 十一、总结
      • 11.1核心要点回顾
    • 十二、推荐资源

目录

前言

你是否还在为权限管理发愁?手写拦截器、过滤器、权限判断?Spring Security 来拯救你了!

作为 Java 生态中最强大的安全框架,Spring Security 为企业级应用提供了完整的安全解决方案。本文带你从零掌握 Spring Security。 在这里插入图片描述


一、Spring Security 是什么?

Spring Security 是一个功能强大且高度可定制的认证和访问控制框架,专注于为 Java 应用程序提供安全服务。

1.1 核心功能

功能说明
认证 (Authentication) 验证"你是谁"
授权 (Authorization) 验证"你能做什么"
防护常见攻击 CSRF、XSS、会话固定等

二、技术组成分布

#mermaid-svg-LQvXvlwP0oWbAN6v{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-LQvXvlwP0oWbAN6v .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-LQvXvlwP0oWbAN6v .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-LQvXvlwP0oWbAN6v .error-icon{fill:#552222;}#mermaid-svg-LQvXvlwP0oWbAN6v .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-LQvXvlwP0oWbAN6v .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-LQvXvlwP0oWbAN6v .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-LQvXvlwP0oWbAN6v .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-LQvXvlwP0oWbAN6v .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-LQvXvlwP0oWbAN6v .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-LQvXvlwP0oWbAN6v .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-LQvXvlwP0oWbAN6v .marker{fill:#333333;stroke:#333333;}#mermaid-svg-LQvXvlwP0oWbAN6v .marker.cross{stroke:#333333;}#mermaid-svg-LQvXvlwP0oWbAN6v svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-LQvXvlwP0oWbAN6v p{margin:0;}#mermaid-svg-LQvXvlwP0oWbAN6v .pieCircle{stroke:#000000;stroke-width:2px;opacity:0.7;}#mermaid-svg-LQvXvlwP0oWbAN6v .pieOuterCircle{stroke:#000000;stroke-width:1px;fill:none;}#mermaid-svg-LQvXvlwP0oWbAN6v .pieTitleText{text-anchor:middle;font-size:25px;fill:#000000;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}#mermaid-svg-LQvXvlwP0oWbAN6v .slice{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;fill:#000000;font-size:17px;}#mermaid-svg-LQvXvlwP0oWbAN6v .legend text{fill:#000000;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:17px;}#mermaid-svg-LQvXvlwP0oWbAN6v :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

30%

25%

20%

15%

10%

Spring Security 学习重点分布

认证机制 [30]

授权控制 [25]

过滤器链 [20]

JWT集成 [15]

OAuth2 [10]


三、认证流程解析

3.1 请求处理流程

#mermaid-svg-O4FZnxZvbM5x5neh{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-O4FZnxZvbM5x5neh .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-O4FZnxZvbM5x5neh .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-O4FZnxZvbM5x5neh .error-icon{fill:#552222;}#mermaid-svg-O4FZnxZvbM5x5neh .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-O4FZnxZvbM5x5neh .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-O4FZnxZvbM5x5neh .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-O4FZnxZvbM5x5neh .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-O4FZnxZvbM5x5neh .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-O4FZnxZvbM5x5neh .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-O4FZnxZvbM5x5neh .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-O4FZnxZvbM5x5neh .marker{fill:#333333;stroke:#333333;}#mermaid-svg-O4FZnxZvbM5x5neh .marker.cross{stroke:#333333;}#mermaid-svg-O4FZnxZvbM5x5neh svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-O4FZnxZvbM5x5neh p{margin:0;}#mermaid-svg-O4FZnxZvbM5x5neh .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-O4FZnxZvbM5x5neh .cluster-label text{fill:#333;}#mermaid-svg-O4FZnxZvbM5x5neh .cluster-label span{color:#333;}#mermaid-svg-O4FZnxZvbM5x5neh .cluster-label span p{background-color:transparent;}#mermaid-svg-O4FZnxZvbM5x5neh .label text,#mermaid-svg-O4FZnxZvbM5x5neh span{fill:#333;color:#333;}#mermaid-svg-O4FZnxZvbM5x5neh .node rect,#mermaid-svg-O4FZnxZvbM5x5neh .node circle,#mermaid-svg-O4FZnxZvbM5x5neh .node ellipse,#mermaid-svg-O4FZnxZvbM5x5neh .node polygon,#mermaid-svg-O4FZnxZvbM5x5neh .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-O4FZnxZvbM5x5neh .rough-node .label text,#mermaid-svg-O4FZnxZvbM5x5neh .node .label text,#mermaid-svg-O4FZnxZvbM5x5neh .image-shape .label,#mermaid-svg-O4FZnxZvbM5x5neh .icon-shape .label{text-anchor:middle;}#mermaid-svg-O4FZnxZvbM5x5neh .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-O4FZnxZvbM5x5neh .rough-node .label,#mermaid-svg-O4FZnxZvbM5x5neh .node .label,#mermaid-svg-O4FZnxZvbM5x5neh .image-shape .label,#mermaid-svg-O4FZnxZvbM5x5neh .icon-shape .label{text-align:center;}#mermaid-svg-O4FZnxZvbM5x5neh .node.clickable{cursor:pointer;}#mermaid-svg-O4FZnxZvbM5x5neh .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-O4FZnxZvbM5x5neh .arrowheadPath{fill:#333333;}#mermaid-svg-O4FZnxZvbM5x5neh .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-O4FZnxZvbM5x5neh .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-O4FZnxZvbM5x5neh .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-O4FZnxZvbM5x5neh .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-O4FZnxZvbM5x5neh .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-O4FZnxZvbM5x5neh .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-O4FZnxZvbM5x5neh .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-O4FZnxZvbM5x5neh .cluster text{fill:#333;}#mermaid-svg-O4FZnxZvbM5x5neh .cluster span{color:#333;}#mermaid-svg-O4FZnxZvbM5x5neh div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-O4FZnxZvbM5x5neh .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-O4FZnxZvbM5x5neh rect.text{fill:none;stroke-width:0;}#mermaid-svg-O4FZnxZvbM5x5neh .icon-shape,#mermaid-svg-O4FZnxZvbM5x5neh .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-O4FZnxZvbM5x5neh .icon-shape p,#mermaid-svg-O4FZnxZvbM5x5neh .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-O4FZnxZvbM5x5neh .icon-shape rect,#mermaid-svg-O4FZnxZvbM5x5neh .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-O4FZnxZvbM5x5neh .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-O4FZnxZvbM5x5neh .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-O4FZnxZvbM5x5neh :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

用户请求

Security Filter Chain

已认证?

认证过滤器

登录验证

成功?

创建SecurityContext

返回401

有权限?

访问资源

返回403

3.2 过滤器链结构

#mermaid-svg-BLyXhcFtbs9G2wxx{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-BLyXhcFtbs9G2wxx .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-BLyXhcFtbs9G2wxx .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-BLyXhcFtbs9G2wxx .error-icon{fill:#552222;}#mermaid-svg-BLyXhcFtbs9G2wxx .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-BLyXhcFtbs9G2wxx .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-BLyXhcFtbs9G2wxx .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-BLyXhcFtbs9G2wxx .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-BLyXhcFtbs9G2wxx .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-BLyXhcFtbs9G2wxx .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-BLyXhcFtbs9G2wxx .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-BLyXhcFtbs9G2wxx .marker{fill:#333333;stroke:#333333;}#mermaid-svg-BLyXhcFtbs9G2wxx .marker.cross{stroke:#333333;}#mermaid-svg-BLyXhcFtbs9G2wxx svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-BLyXhcFtbs9G2wxx p{margin:0;}#mermaid-svg-BLyXhcFtbs9G2wxx .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-BLyXhcFtbs9G2wxx .cluster-label text{fill:#333;}#mermaid-svg-BLyXhcFtbs9G2wxx .cluster-label span{color:#333;}#mermaid-svg-BLyXhcFtbs9G2wxx .cluster-label span p{background-color:transparent;}#mermaid-svg-BLyXhcFtbs9G2wxx .label text,#mermaid-svg-BLyXhcFtbs9G2wxx span{fill:#333;color:#333;}#mermaid-svg-BLyXhcFtbs9G2wxx .node rect,#mermaid-svg-BLyXhcFtbs9G2wxx .node circle,#mermaid-svg-BLyXhcFtbs9G2wxx .node ellipse,#mermaid-svg-BLyXhcFtbs9G2wxx .node polygon,#mermaid-svg-BLyXhcFtbs9G2wxx .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-BLyXhcFtbs9G2wxx .rough-node .label text,#mermaid-svg-BLyXhcFtbs9G2wxx .node .label text,#mermaid-svg-BLyXhcFtbs9G2wxx .image-shape .label,#mermaid-svg-BLyXhcFtbs9G2wxx .icon-shape .label{text-anchor:middle;}#mermaid-svg-BLyXhcFtbs9G2wxx .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-BLyXhcFtbs9G2wxx .rough-node .label,#mermaid-svg-BLyXhcFtbs9G2wxx .node .label,#mermaid-svg-BLyXhcFtbs9G2wxx .image-shape .label,#mermaid-svg-BLyXhcFtbs9G2wxx .icon-shape .label{text-align:center;}#mermaid-svg-BLyXhcFtbs9G2wxx .node.clickable{cursor:pointer;}#mermaid-svg-BLyXhcFtbs9G2wxx .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-BLyXhcFtbs9G2wxx .arrowheadPath{fill:#333333;}#mermaid-svg-BLyXhcFtbs9G2wxx .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-BLyXhcFtbs9G2wxx .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-BLyXhcFtbs9G2wxx .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-BLyXhcFtbs9G2wxx .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-BLyXhcFtbs9G2wxx .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-BLyXhcFtbs9G2wxx .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-BLyXhcFtbs9G2wxx .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-BLyXhcFtbs9G2wxx .cluster text{fill:#333;}#mermaid-svg-BLyXhcFtbs9G2wxx .cluster span{color:#333;}#mermaid-svg-BLyXhcFtbs9G2wxx div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-BLyXhcFtbs9G2wxx .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-BLyXhcFtbs9G2wxx rect.text{fill:none;stroke-width:0;}#mermaid-svg-BLyXhcFtbs9G2wxx .icon-shape,#mermaid-svg-BLyXhcFtbs9G2wxx .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-BLyXhcFtbs9G2wxx .icon-shape p,#mermaid-svg-BLyXhcFtbs9G2wxx .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-BLyXhcFtbs9G2wxx .icon-shape rect,#mermaid-svg-BLyXhcFtbs9G2wxx .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-BLyXhcFtbs9G2wxx .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-BLyXhcFtbs9G2wxx .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-BLyXhcFtbs9G2wxx :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

请求

ChannelProcessingFilter

SecurityContextFilter

LogoutFilter

UsernamePasswordAuthenticationFilter

ExceptionTranslationFilter

FilterSecurityInterceptor

Controller


四、快速开始

4.1 添加依赖

<!– pom.xml –>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>

4.2 基础配置

@Configuration
@EnableWebSecurity
public class SecurityConfig {

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/public/**").permitAll()
.anyRequest().authenticated()
)
.formLogin(withDefaults())
.httpBasic(withDefaults());
return http.build();
}
}


五、认证机制详解

5.1 内存认证(开发测试)

@Bean
public UserDetailsService userDetailsService() {
UserDetails user = User.builder()
.username("admin")
.password(passwordEncoder().encode("123456"))
.roles("ADMIN")
.build();

UserDetails guest = User.builder()
.username("guest")
.password(passwordEncoder().encode("123456"))
.roles("GUEST")
.build();

return new InMemoryUserDetailsManager(user, guest);
}

@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}

5.2 数据库认证(生产推荐)

@Service
public class CustomUserDetailsService implements UserDetailsService {

@Autowired
private UserRepository userRepository;

@Override
public UserDetails loadUserByUsername(String username) {
User user = userRepository.findByUsername(username)
.orElseThrow(() -> new UsernameNotFoundException("用户不存在"));

return User.withUsername(user.getUsername())
.password(user.getPassword())
.roles(user.getRole())
.build();
}
}


六、授权控制方式

6.1 注解方式

// 启用注解
@EnableGlobalMethodSecurity(prePostEnabled = true)

// 使用示例
@Service
public class UserService {

@PreAuthorize("hasRole('ADMIN')")
public void deleteUser(Long id) {
// 只有ADMIN角色可以删除
}

@PreAuthorize("#userId == authentication.principal.id")
public User getUserInfo(Long userId) {
// 只能查询自己的信息
}

@PostFilter("filterObject.owner == authentication.name")
public List<Document> getAllDocuments() {
// 过滤返回结果
}
}

6.2 配置方式

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/admin/**").hasRole("ADMIN")
.requestMatchers("/api/user/**").hasAnyRole("ADMIN", "USER")
.requestMatchers(HttpMethod.POST, "/api/posts").hasAuthority("post:write")
.requestMatchers("/public/**").permitAll()
.anyRequest().authenticated()
);
return http.build();
}


七、JWT 无状态认证

7.1 JWT 认证流程

资源

服务器

用户

资源

服务器

用户

#mermaid-svg-bdTHXWwInETfZOrb{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-bdTHXWwInETfZOrb .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-bdTHXWwInETfZOrb .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-bdTHXWwInETfZOrb .error-icon{fill:#552222;}#mermaid-svg-bdTHXWwInETfZOrb .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-bdTHXWwInETfZOrb .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-bdTHXWwInETfZOrb .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-bdTHXWwInETfZOrb .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-bdTHXWwInETfZOrb .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-bdTHXWwInETfZOrb .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-bdTHXWwInETfZOrb .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-bdTHXWwInETfZOrb .marker{fill:#333333;stroke:#333333;}#mermaid-svg-bdTHXWwInETfZOrb .marker.cross{stroke:#333333;}#mermaid-svg-bdTHXWwInETfZOrb svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-bdTHXWwInETfZOrb p{margin:0;}#mermaid-svg-bdTHXWwInETfZOrb .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-bdTHXWwInETfZOrb text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-bdTHXWwInETfZOrb .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-bdTHXWwInETfZOrb .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-bdTHXWwInETfZOrb .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-bdTHXWwInETfZOrb .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-bdTHXWwInETfZOrb #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-bdTHXWwInETfZOrb .sequenceNumber{fill:white;}#mermaid-svg-bdTHXWwInETfZOrb #sequencenumber{fill:#333;}#mermaid-svg-bdTHXWwInETfZOrb #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-bdTHXWwInETfZOrb .messageText{fill:#333;stroke:none;}#mermaid-svg-bdTHXWwInETfZOrb .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-bdTHXWwInETfZOrb .labelText,#mermaid-svg-bdTHXWwInETfZOrb .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-bdTHXWwInETfZOrb .loopText,#mermaid-svg-bdTHXWwInETfZOrb .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-bdTHXWwInETfZOrb .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-bdTHXWwInETfZOrb .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-bdTHXWwInETfZOrb .noteText,#mermaid-svg-bdTHXWwInETfZOrb .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-bdTHXWwInETfZOrb .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-bdTHXWwInETfZOrb .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-bdTHXWwInETfZOrb .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-bdTHXWwInETfZOrb .actorPopupMenu{position:absolute;}#mermaid-svg-bdTHXWwInETfZOrb .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-bdTHXWwInETfZOrb .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-bdTHXWwInETfZOrb .actor-man circle,#mermaid-svg-bdTHXWwInETfZOrb line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-bdTHXWwInETfZOrb :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

POST /login {username, password}

验证用户信息

返回JWT Token

GET /api/data (Header: Authorization: Bearer <token>)

验证Token

返回数据

7.2 JWT 实现

// JWT 工具类
@Component
public class JwtUtil {

@Value("${jwt.secret}")
private String secret;

public String generateToken(UserDetails userDetails) {
return Jwts.builder()
.setSubject(userDetails.getUsername())
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + 86400000))
.signWith(SignatureAlgorithm.HS256, secret)
.compact();
}

public boolean validateToken(String token, UserDetails userDetails) {
return extractUsername(token).equals(userDetails.getUsername())
&& !isTokenExpired(token);
}
}

// JWT 认证过滤器
public class JwtAuthenticationFilter extends OncePerRequestFilter {

@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain chain) {
String token = extractToken(request);
if (token != null && jwtUtil.validateToken(token, userDetails)) {
UsernamePasswordAuthenticationToken auth =
new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(auth);
}
chain.doFilter(request, response);
}
}


八、常见场景解决方案

8.1 场景一:记住我功能

http
.rememberMe(remember -> remember
.key("unique-and-secret")
.tokenValiditySeconds(86400)
.userDetailsService(userDetailsService)
);

8.2 场景二:退出登录

http
.logout(logout -> logout
.logoutUrl("/logout")
.logoutSuccessUrl("/login?logout")
.deleteCookies("JSESSIONID")
.addLogoutHandler(new SecurityContextLogoutHandler())
);

8.3 场景三:CSRF 防护

http
.csrf(csrf -> csrf
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
// API接口可以禁用CSRF
.ignoringRequestMatchers("/api/**")
);

8.4 场景四:自定义登录页

http
.formLogin(form -> form
.loginPage("/login")
.defaultSuccessUrl("/dashboard")
.permitAll()
);


九、学习路线

#mermaid-svg-IxqcqUHT2aSVDiCU{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-IxqcqUHT2aSVDiCU .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-IxqcqUHT2aSVDiCU .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-IxqcqUHT2aSVDiCU .error-icon{fill:#552222;}#mermaid-svg-IxqcqUHT2aSVDiCU .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-IxqcqUHT2aSVDiCU .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-IxqcqUHT2aSVDiCU .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-IxqcqUHT2aSVDiCU .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-IxqcqUHT2aSVDiCU .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-IxqcqUHT2aSVDiCU .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-IxqcqUHT2aSVDiCU .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-IxqcqUHT2aSVDiCU .marker{fill:#333333;stroke:#333333;}#mermaid-svg-IxqcqUHT2aSVDiCU .marker.cross{stroke:#333333;}#mermaid-svg-IxqcqUHT2aSVDiCU svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-IxqcqUHT2aSVDiCU p{margin:0;}#mermaid-svg-IxqcqUHT2aSVDiCU .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-IxqcqUHT2aSVDiCU .cluster-label text{fill:#333;}#mermaid-svg-IxqcqUHT2aSVDiCU .cluster-label span{color:#333;}#mermaid-svg-IxqcqUHT2aSVDiCU .cluster-label span p{background-color:transparent;}#mermaid-svg-IxqcqUHT2aSVDiCU .label text,#mermaid-svg-IxqcqUHT2aSVDiCU span{fill:#333;color:#333;}#mermaid-svg-IxqcqUHT2aSVDiCU .node rect,#mermaid-svg-IxqcqUHT2aSVDiCU .node circle,#mermaid-svg-IxqcqUHT2aSVDiCU .node ellipse,#mermaid-svg-IxqcqUHT2aSVDiCU .node polygon,#mermaid-svg-IxqcqUHT2aSVDiCU .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-IxqcqUHT2aSVDiCU .rough-node .label text,#mermaid-svg-IxqcqUHT2aSVDiCU .node .label text,#mermaid-svg-IxqcqUHT2aSVDiCU .image-shape .label,#mermaid-svg-IxqcqUHT2aSVDiCU .icon-shape .label{text-anchor:middle;}#mermaid-svg-IxqcqUHT2aSVDiCU .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-IxqcqUHT2aSVDiCU .rough-node .label,#mermaid-svg-IxqcqUHT2aSVDiCU .node .label,#mermaid-svg-IxqcqUHT2aSVDiCU .image-shape .label,#mermaid-svg-IxqcqUHT2aSVDiCU .icon-shape .label{text-align:center;}#mermaid-svg-IxqcqUHT2aSVDiCU .node.clickable{cursor:pointer;}#mermaid-svg-IxqcqUHT2aSVDiCU .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-IxqcqUHT2aSVDiCU .arrowheadPath{fill:#333333;}#mermaid-svg-IxqcqUHT2aSVDiCU .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-IxqcqUHT2aSVDiCU .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-IxqcqUHT2aSVDiCU .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-IxqcqUHT2aSVDiCU .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-IxqcqUHT2aSVDiCU .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-IxqcqUHT2aSVDiCU .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-IxqcqUHT2aSVDiCU .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-IxqcqUHT2aSVDiCU .cluster text{fill:#333;}#mermaid-svg-IxqcqUHT2aSVDiCU .cluster span{color:#333;}#mermaid-svg-IxqcqUHT2aSVDiCU div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-IxqcqUHT2aSVDiCU .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-IxqcqUHT2aSVDiCU rect.text{fill:none;stroke-width:0;}#mermaid-svg-IxqcqUHT2aSVDiCU .icon-shape,#mermaid-svg-IxqcqUHT2aSVDiCU .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-IxqcqUHT2aSVDiCU .icon-shape p,#mermaid-svg-IxqcqUHT2aSVDiCU .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-IxqcqUHT2aSVDiCU .icon-shape rect,#mermaid-svg-IxqcqUHT2aSVDiCU .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-IxqcqUHT2aSVDiCU .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-IxqcqUHT2aSVDiCU .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-IxqcqUHT2aSVDiCU :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

Spring Security 入门

理解核心概念

认证机制

授权控制

JWT集成

OAuth2/SAML

微服务安全

实战项目

9.1 学习时间建议

阶段内容时间
入门 基本概念、配置 1周
进阶 认证授权、自定义 2周
高级 JWT、OAuth2 2周
实战 项目应用 持续

十、最佳实践

10.1 密码加密

// 始终使用 BCrypt
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}

10.2 权限设计原则

// 推荐:基于角色的权限访问控制 (RBAC)
@PreAuthorize("hasRole('ADMIN') and hasAuthority('user:delete')")

// 不推荐:硬编码
@PreAuthorize("hasRole('ADMIN') or username == 'admin'")

10.3 异常处理

@RestControllerAdvice
public class SecurityExceptionHandler {

@ExceptionHandler(AccessDeniedException.class)
public ResponseEntity<?> handleAccessDenied(AccessDeniedException e) {
return ResponseEntity.status(403)
.body("没有权限访问该资源");
}
}

10.4 安全配置

// 生产环境必须配置
http
.headers(headers -> headers
.contentSecurityPolicy(csp -> csp.policyDirectives("default-src 'self'"))
.frameOptions(frame -> frame.sameOrigin())
.httpStrictTransportSecurity(hsts -> hsts
.includeSubDomains(true)
.maxAgeInSeconds(31536000)
)
);


十一、总结

Spring Security 虽然学习曲线陡峭,但一旦掌握,将极大提升应用的安全性和开发效率。

11.1核心要点回顾

要点说明
🔐 认证 验证用户身份
🎯 授权 控制访问权限
🔗 过滤器链 请求处理核心
🎫 JWT 无状态认证方案
🛡️ 防护 抵御常见攻击

十二、推荐资源

资源链接
官方文档 https://spring.io/projects/spring-security
Baeldung教程 https://www.baeldung.com/spring-security
中文手册 https://springcloud.cc/spring-security.html

💡 一句话总结:Spring Security 是企业级 Java 应用的安全基石,值得深入学习和掌握!

赞(0)
未经允许不得转载:171主机测评 » Spring Security 从入门到实战:搞定认证授权,再也不用手写权限逻辑
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址