当前位置: 首页 > news >正文

Spring Security 新手学习教程

这是一份非常实用的 Spring Security 教程,从零开始带你掌握这个强大的安全框架。

一、Spring Security 是什么?

Spring Security 是一个功能强大且高度可定制的身份认证和访问控制框架,是 Spring 家族中保护 Java 应用的事实标准。

核心功能:

  • 认证:验证用户身份(登录)

  • 授权:控制用户访问权限

  • 防护:防止常见攻击(CSRF、XSS 等)


二、快速开始

1. 添加依赖

Maven:

<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.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!-- Thymeleaf Security 整合 -->
<dependency><groupId>org.thymeleaf.extras</groupId><artifactId>thymeleaf-extras-springsecurity6</artifactId>
</dependency>

2. 最小配置示例

只要加入依赖,Spring Security 就会自动生效,默认配置:

  • 所有接口都需要认证

  • 自动生成登录页面

  • 默认用户:user

  • 默认密码:在控制台日志中查看

访问任何页面都会跳转到自动生成的登录页。


三、基础配置

1. 内存认证配置

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
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;
import org.springframework.security.web.SecurityFilterChain;@Configuration
@EnableWebSecurity
public class SecurityConfig {@Beanpublic SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {http.authorizeHttpRequests(auth -> auth.requestMatchers("/", "/home", "/public/**").permitAll() // 允许匿名访问.requestMatchers("/admin/**").hasRole("ADMIN") // 需要 ADMIN 角色.requestMatchers("/user/**").hasAnyRole("USER", "ADMIN") // 需要 USER 或 ADMIN 角色.anyRequest().authenticated() // 其他所有请求都需要认证).formLogin(form -> form.loginPage("/login") // 自定义登录页.permitAll() // 登录页允许所有人访问).logout(logout -> logout.permitAll() // 允许所有人退出登录);return http.build();}@Beanpublic UserDetailsService userDetailsService() {UserDetails user = User.builder().username("user").password(passwordEncoder().encode("password")).roles("USER").build();UserDetails admin = User.builder().username("admin").password(passwordEncoder().encode("admin")).roles("ADMIN", "USER").build();return new InMemoryUserDetailsManager(user, admin);}@Beanpublic PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();}
}

2. 自定义登录页

Controller:

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;@Controller
public class LoginController {@GetMapping("/login")public String login() {return "login";}@GetMapping("/")public String home() {return "home";}@GetMapping("/admin/dashboard")public String adminDashboard() {return "admin/dashboard";}@GetMapping("/user/profile")public String userProfile() {return "user/profile";}
}

登录页面 templates/login.html:

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head><meta charset="UTF-8"><title>登录</title><style>.login-container { max-width: 400px; margin: 100px auto; padding: 20px; }.form-group { margin-bottom: 15px; }label { display: block; margin-bottom: 5px; }input[type="text"], input[type="password"] { width: 100%; padding: 8px; border: 1px solid #ddd; }button { width: 100%; padding: 10px; background: #007bff; color: white; border: none; }.error { color: red; margin-top: 10px; }</style>
</head>
<body><div class="login-container"><h2>用户登录</h2><!-- 显示错误信息 --><div th:if="${param.error}" class="error">用户名或密码错误!</div><!-- 显示退出信息 --><div th:if="${param.logout}" class="error" style="color: green;">您已成功退出登录!</div><form th:action="@{/login}" method="post"><div class="form-group"><label>用户名:</label><input type="text" name="username" required></div><div class="form-group"><label>密码:</label><input type="password" name="password" required></div><button type="submit">登录</button></form></div>
</body>
</html>

四、数据库认证(实际项目用法)

1. 实体类和 Repository

User 实体:

import lombok.Data;
import javax.persistence.*;
import java.util.List;@Data
@Entity
@Table(name = "users")
public class User {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long id;@Column(unique = true, nullable = false)private String username;@Column(nullable = false)private String password;private boolean enabled = true;@ManyToMany(fetch = FetchType.EAGER)@JoinTable(name = "user_roles",joinColumns = @JoinColumn(name = "user_id"),inverseJoinColumns = @JoinColumn(name = "role_id"))private List<Role> roles;
}

Role 实体:

import lombok.Data;
import javax.persistence.*;@Data
@Entity
@Table(name = "roles")
public class Role {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long id;@Column(unique = true, nullable = false)private String name; // 如:ROLE_USER, ROLE_ADMIN
}

Repository:

public interface UserRepository extends JpaRepository<User, Long> {Optional<User> findByUsername(String username);boolean existsByUsername(String username);
}

2. 自定义 UserDetailsService

import lombok.RequiredArgsConstructor;
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;@Service
@RequiredArgsConstructor
public class CustomUserDetailsService implements UserDetailsService {private final UserRepository userRepository;@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {User user = userRepository.findByUsername(username).orElseThrow(() -> new UsernameNotFoundException("用户不存在: " + username));return org.springframework.security.core.userdetails.User.builder().username(user.getUsername()).password(user.getPassword()).disabled(!user.isEnabled()).authorities(user.getRoles().stream().map(role -> role.getName()).toArray(String[]::new)).build();}
}

3. 安全配置(数据库版)

@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig {private final CustomUserDetailsService userDetailsService;@Beanpublic SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {http.authorizeHttpRequests(auth -> auth.requestMatchers("/css/**", "/js/**", "/images/**").permitAll().requestMatchers("/", "/home", "/register").permitAll().requestMatchers("/admin/**").hasRole("ADMIN").requestMatchers("/user/**").hasAnyRole("USER", "ADMIN").anyRequest().authenticated()).formLogin(form -> form.loginPage("/login").defaultSuccessUrl("/dashboard") // 登录成功后跳转.permitAll()).logout(logout -> logout.logoutSuccessUrl("/?logout=true").permitAll()).rememberMe(remember -> remember.key("uniqueAndSecret").tokenValiditySeconds(86400) // 1天).userDetailsService(userDetailsService);return http.build();}@Beanpublic PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();}
}

五、密码加密

密码加密最佳实践

Service 中的密码处理:

@Service
@RequiredArgsConstructor
public class UserService {private final UserRepository userRepository;private final PasswordEncoder passwordEncoder;private final RoleRepository roleRepository;public User registerUser(RegisterRequest request) {if (userRepository.existsByUsername(request.getUsername())) {throw new RuntimeException("用户名已存在");}User user = new User();user.setUsername(request.getUsername());user.setPassword(passwordEncoder.encode(request.getPassword())); // 加密密码user.setEnabled(true);// 分配默认角色Role userRole = roleRepository.findByName("ROLE_USER").orElseThrow(() -> new RuntimeException("角色不存在"));user.setRoles(List.of(userRole));return userRepository.save(user);}
}

六、Thymeleaf 整合

在页面中获取用户信息和权限

首页 templates/home.html:

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<head><meta charset="UTF-8"><title>首页</title>
</head>
<body><h1>欢迎来到首页</h1><!-- 显示当前登录用户信息 --><div sec:authorize="isAuthenticated()"><p>欢迎, <span sec:authentication="name">用户</span>!</p><p>你的角色: <span sec:authentication="principal.authorities">权限</span></p><!-- 退出表单 --><form th:action="@{/logout}" method="post"><button type="submit">退出登录</button></form></div><!-- 未登录时显示 --><div sec:authorize="!isAuthenticated()"><a th:href="@{/login}">登录</a> | <a th:href="@{/register}">注册</a></div><hr><!-- 根据权限显示不同链接 --><nav><a th:href="@{/}">首页</a> |<!-- 只有 USER 角色能看到 --><a th:href="@{/user/profile}" sec:authorize="hasRole('USER')">个人资料</a> |<!-- 只有 ADMIN 角色能看到 --><a th:href="@{/admin/dashboard}" sec:authorize="hasRole('ADMIN')">管理后台</a> |<!-- 有 USER 或 ADMIN 角色都能看到 --><a th:href="@{/user/settings}" sec:authorize="hasAnyRole('USER', 'ADMIN')">设置</a></nav><!-- 条件显示内容 --><div sec:authorize="hasRole('ADMIN')"><h3>管理员专属区域</h3><p>只有管理员能看到这个内容。</p></div>
</body>
</html>

七、方法级安全控制

1. 启用方法安全

@Configuration
@EnableWebSecurity
@EnableMethodSecurity(prePostEnabled = true) // 启用方法级安全
public class SecurityConfig {// ... 其他配置
}

2. 在 Service 方法上使用注解

@Service
public class UserService {// 只有 ADMIN 可以调用此方法@PreAuthorize("hasRole('ADMIN')")public List<User> getAllUsers() {return userRepository.findAll();}// 只能查看自己的资料,或 ADMIN 可以查看所有@PreAuthorize("hasRole('ADMIN') or authentication.name == #username")public User getUserProfile(String username) {return userRepository.findByUsername(username).orElseThrow(() -> new RuntimeException("用户不存在"));}// 只有 ADMIN 或资源所有者可以更新@PreAuthorize("hasRole('ADMIN') or #user.id == authentication.principal.id")public User updateUser(User user) {return userRepository.save(user);}// 后置权限检查@PostAuthorize("returnObject.username == authentication.name or hasRole('ADMIN')")public User findById(Long id) {return userRepository.findById(id).orElse(null);}
}

八、常见攻击防护

CSRF 防护(默认开启)

在表单中添加 CSRF token:

<form th:action="@{/user/update}" method="post"><input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}" /><!-- 其他表单字段 --><button type="submit">更新</button>
</form>

如果是 REST API,可以禁用 CSRF:

http.csrf(csrf -> csrf.disable()); // 不推荐在 Web 应用中禁用

九、实践建议

1. 配置文件分离

# application.yml
spring:security:user:name: adminpassword: ${ADMIN_PASSWORD:defaultPass}

2. 自定义访问拒绝处理

@Component
public class CustomAccessDeniedHandler implements AccessDeniedHandler {@Overridepublic void handle(HttpServletRequest request, HttpServletResponse response,AccessDeniedException accessDeniedException) throws IOException {response.sendRedirect("/access-denied");}
}// 在配置中使用
http.exceptionHandling(exception -> exception.accessDeniedHandler(customAccessDeniedHandler)
);

3. 登录成功处理

@Component
public class CustomAuthenticationSuccessHandler implements AuthenticationSuccessHandler {@Overridepublic void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response,Authentication authentication) throws IOException {// 根据角色跳转到不同页面if (authentication.getAuthorities().stream().anyMatch(a -> a.getAuthority().equals("ROLE_ADMIN"))) {response.sendRedirect("/admin/dashboard");} else {response.sendRedirect("/user/dashboard");}}
}

十、学习路径建议

  1. 第一步:掌握基础配置和内存认证

  2. 第二步:学习数据库认证和密码加密

  3. 第三步:掌握 Thymeleaf 整合和页面权限控制

  4. 第四步:学习方法级安全控制

  5. 第五步:研究 OAuth2、JWT 等高级特性

http://www.dtcms.com/a/552750.html

相关文章:

  • 72.是否可以把所有Bean都通过Spring容器来管
  • DevExpress WPF中文教程:Data Grid - 如何使用虚拟源?(四)
  • 车载软件需求开发与管理 --- 需求收集与整理
  • [linux仓库]线程控制[线程·叁]
  • 从工行“余额归零”事件看CAP定理:当金融系统在一致性与可用性之间做出选择
  • Java的stream使用方案
  • 给网站做视频怎么赚钱电影网站系统源码
  • React Server Components 进阶:数据预取与缓存
  • MR30分布式I/O助力物流分拣系统智能化升级
  • 当UAF漏洞敲响提权警钟:技术剖析与应对之道
  • Flink(用Scala版本写Word Count 出现假报错情况解决方案)假报错,一直显示红色报错
  • Smartbi 10 月版本亮点:AIChat对话能力提升,国产化部署更安全
  • 网站备案单位商业网站源码免费下载
  • 外贸网站经典营销案例搭建服务器做网站
  • MQTT 协议详解与工业物联网架构设计指南
  • JMeter WebSocket异步接口测试简明指南
  • [论文]Colmap-PCD: An Open-source Tool for Fine Image-to-point cloud Registration
  • 网站开发合作协议自主建站系统
  • MySQL 8 查询逗号分隔字符串
  • react 源码2
  • 淮南电商网站建设苏州网站优化
  • AI应用市场崛起:聊天机器人、教育学习、视频创作三驾马车驱动创新
  • SQL 学习笔记
  • 医药网站建设中图片app开发公司 弙东
  • ProfiNet转ModbusTCP实战:工业智能网关让S7-1516与上位机3ms握手
  • 巨 椰 云手机和云真机哪个个比较好用?
  • 云手机中的数据信息会被泄露吗
  • 百度快照举报网站威海企业网站建设
  • 16.React性能优化SCU
  • Linux系统C++开发环境搭建工具(三)—— brpc使用指南