综合项目(二):FastAPI编写CRUD接口

核心内容摘要

3步攻克ComfyUI ControlNet Aux模型下载难题:从故障排查到性能优化
如何突破微服务开发瓶颈?Pig框架的企业级解决方案探索

如何在一天内改变你的整个人生(英中对照)• VII

Spring Security 概述与核心概念

1 什么是Spring SecuritySpring Security 是一个功能强大且高度可定制的身份验证Authentication和访问控制Authorization框架。

作为 Spring 生态中事实上的安全标准它为基于 Spring 的应用程序提供了声明式的安全防护解决方案。

核心价值在于认证Authentication解决“你是谁”的问题即验证用户身份是否合法。

常见方式包括用户名密码、短信验证码、二维码、指纹等。

授权Authorization解决“你能做什么”的问题即根据用户权限控制其对系统资源的访问。

Spring Security 在架构上清晰地将认证与授权分离并提供了丰富的扩展点便于开发者根据业务需求进行定制。

2 Spring Security vs Apache Shiro在Java安全框架领域Spring Security 与 Apache Shiro 是两大主流选择。

相同点均提供认证、授权、加密、会话管理、缓存支持及Remember-Me功能。

不同点特性Spring SecurityApache Shiro与Spring集成天然集成无缝配合需要额外整合功能丰富度更丰富提供完整的安全防护体系核心功能完善但高级特性较少学习曲线较陡峭配置复杂简单易懂上手快依赖性强依赖Spring容器轻量可独立运行社区资源非常丰富相对较少技术选型建议传统SSM项目可选用Shiro轻量且易集成。

Spring Boot/Spring Cloud项目强烈推荐Spring Security享受其与Spring生态的完美融合和持续增强的安全特性。

Spring Security 核心实战用户认证

1 快速入门

引入依赖创建一个Spring Boot项目并添加以下依赖xmldependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-security/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency

编写测试接口javaRestController RequestMapping(/admin) public class AdminController { GetMapping(/demo) public String demo() { return spring security demo; } }

启动并测试引入依赖后访问http://localhost:8080/admin/demoSpring Security 会自动重定向到其默认登录页。

默认用户名user默认密码在应用启动控制台日志中查找。

登录后即可访问接口。

退出访问http://localhost:8080/logout。

2 配置用户信息方式一application.yml配置 (适用于简单场景)yamlspring: security: user: name: user # 用户名 password: 123456 # 密码 roles: # 角色 - admin原理UserDetailsServiceAutoConfiguration会根据此配置在内存中创建对应用户。

方式二Java Config配置 (更灵活)javaConfiguration EnableWebSecurity public class SecurityConfig { Bean public UserDetailsService userDetailsService() { // 使用默认的bcrypt加密 UserDetails user User.withDefaultPasswordEncoder() .username(fox) .password(

.roles(user) .build(); // 密码不加密 UserDetails admin User.withUsername(admin) .password({noop}

.roles(admin, user) .build(); return new InMemoryUserDetailsManager(user, admin); } }

3 密码编码器Spring Security 密码格式为{id}encodedPassword。

{bcrypt}: BCrypt加密{noop}: 不加密明文{pbkdf2}: PBKDF2加密可通过PasswordEncoderBean 统一指定加密方式javaBean public PasswordEncoder passwordEncoder(){ // return NoopPasswordEncoder.getInstance(); // 不加密 return new BCryptPasswordEncoder(); // 使用BCrypt加密 } // 使用编码器 UserDetails admin User.withUsername(admin) .password(passwordEncoder().encode(

) .roles(admin, user) .build();

4 自定义用户来源如数据库实现UserDetailsService接口从数据库加载用户信息。

javaService public class TulingUserDetailService implements UserDetailsService { Autowired private PasswordEncoder passwordEncoder; Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { //

根据用户名从数据库查询用户、角色、权限信息 //

封装成UserDetails对象返回 UserDetails user User .withUsername(fox) .password(passwordEncoder.encode(

) .roles(user) .build(); return user; } }

5 自定义登录页面Spring Security 默认登录页由DefaultLoginPageGeneratingFilter生成。

我们可以自定义。

编写登录页面 (login.html)html!DOCTYPE html html body form action/user/login methodpost 用户名:input typetext nameusername/br/ 密码:input typepassword namepassword/br/ input typesubmit value提交/ /form /body /html

配置安全过滤器链javaBean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { // 表单登录配置 http.formLogin((formLogin) - formLogin .loginPage(/login.html) // 自定义登录页地址 .loginProcessingUrl(/user/login) // 表单提交地址触发UserDetailsService验证 .defaultSuccessUrl(/admin/demo) // 登录成功跳转地址 ); // 授权配置 http.authorizeHttpRequests((authorize) - authorize .requestMatchers(/login.html, /user/login).permitAll() // 放行登录相关路径 .anyRequest().authenticated() // 其他所有请求都需要认证 ); // 关闭CSRF根据实际情况决定 http.csrf((csrf) - csrf.disable()); return http.build(); }

6 前后端分离认证前后端分离项目中登录成功或失败通常返回JSON而非页面跳转。

javahttp.formLogin((formLogin) - formLogin .loginProcessingUrl(/login) .successHandler(new LoginSuccessHandler()) // 登录成功处理器 .failureHandler(new LoginFailureHandler()) // 登录失败处理器 ); // 登录成功处理器 public class LoginSuccessHandler implements AuthenticationSuccessHandler { Override public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException { response.setContentType(application/json;charsetutf-

; // 可在此生成JWT令牌并返回 MapString, Object result new HashMap(); result.put(code,

; result.put(msg, 登录成功); result.put(data, authentication.getPrincipal()); response.getWriter().write(new ObjectMapper().writeValueAsString(result)); } } // 登录失败处理器 public class LoginFailureHandler implements AuthenticationFailureHandler { Override public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException { response.setContentType(application/json;charsetutf-

; MapString, Object result new HashMap(); result.put(code,

; result.put(msg, 登录失败 exception.getMessage()); response.getWriter().write(new ObjectMapper().writeValueAsString(result)); } }

Spring Security 核心实战用户授权授权分为Web授权URL拦截和方法授权注解拦截。

1 Web授权基于URL在SecurityFilterChain配置中使用authorizeHttpRequests。

javahttp.authorizeHttpRequests((authorize) - authorize .requestMatchers(/login).permitAll() .requestMatchers(/index).hasRole(user) // 需要ROLE_user角色 .requestMatchers(/admin/**).hasRole(admin) // 需要ROLE_admin角色 .requestMatchers(/user/**).hasAuthority(user:api) // 需要特定权限字符串 .anyRequest().authenticated() );注意hasRole方法会在传入的字符串前自动添加ROLE_前缀。

而hasAuthority则需要完整的权限字符串。

配置时roles()和authorities()不能同时用于一个用户后者会覆盖前者。

2 自定义授权失败处理实现AccessDeniedHandler接口处理403无权限异常。

javapublic class BusinessAccessDeniedHandler implements AccessDeniedHandler { Override public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException e) throws IOException { response.setContentType(application/json;charsetutf-

; MapString, Object result new HashMap(); result.put(code,

; result.put(msg, 权限不足无法访问); response.getWriter().write(new ObjectMapper().writeValueAsString(result)); } } // 配置 http.exceptionHandling((exception) - exception.accessDeniedHandler(new BusinessAccessDeniedHandler()) );

3 方法授权基于注解首先在配置类上启用注解支持javaConfiguration EnableWebSecurity EnableMethodSecurity(jsr250Enabled true, securedEnabled true) // Spring Security 6 使用 EnableMethodSecurity public class SecurityConfig { }然后在Controller或Service方法上使用注解java// JSR-250注解 RolesAllowed({ROLE_user, ROLE_admin}) GetMapping(/index

public String index5(){ return index5; } // Spring Security原生注解 Secured(ROLE_admin) GetMapping(/index

public String index6(){ return index6; } // 支持表达式的注解功能最强大 PreAuthorize(hasRole(ROLE_admin) and #id

// 方法执行前校验需admin角色且id参数10 GetMapping(/findUserById) public String findById(long id) { return success; }

Spring Security 整合 JWT 实现无状态认证在前后端分离架构中JWTJSON Web Token是实现无状态登录认证的流行方案。

1 JWT 简介JWT (JSON Web Token)是一种开放的行业标准用于安全地在双方之间传输JSON对象信息。

其特点包括优点基于JSON易解析扩展通过数字签名防篡改资源服务可独立完成授权。

缺点令牌较长安全性依赖密钥管理一旦签发在有效期内无法主动撤销。

JWT组成Header (头部)声明令牌类型和签名算法如HS256。

Payload (载荷)存放有效信息如用户ID、过期时间。

Signature (签名)对前两部分进行签名防止数据篡改。

一个JWT格式Header.Payload.Signature应用方式在请求头中添加Authorization: Bearer your_token

2 核心实现JWT认证过滤器我们需要创建一个过滤器在用户名密码认证之前解析并校验请求头中的JWT Token。

javaComponent Slf4j public class JwtAuthenticationTokenFilter extends OncePerRequestFilter { Autowired private UserDetailsService userDetailsService; Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException { //

从请求头获取Token String authHeader request.getHeader(HttpHeaders.AUTHORIZATION); if (!StringUtils.hasText(authHeader) || !authHeader.startsWith(Bearer )) { chain.doFilter(request, response); return; } String token authHeader.substring(

; // 去掉Bearer 前缀 //

解析并校验Token try { MapString, Object claims JWTUtils.parseToken(token); // 自定义工具类 if (JWTUtils.isExpiresIn((long) claims.get(exp))) { // Token已过期清空上下文 SecurityContextHolder.clearContext(); chain.doFilter(request, response); return; } String username (String) claims.get(username); //

如果用户名有效且当前上下文未认证 if (StringUtils.hasText(username) SecurityContextHolder.getContext().getAuthentication() null) { UserDetails userDetails userDetailsService.loadUserByUsername(username); if (userDetails ! null) { //

构建认证对象并存入SecurityContextHolder UsernamePasswordAuthenticationToken authentication new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities()); authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); SecurityContextHolder.getContext().setAuthentication(authentication); log.info(Authenticated user: {}, username); } } } catch (Exception e) { log.error(JWT token验证失败, e); } chain.doFilter(request, response); } }配置过滤器在SecurityFilterChain配置中将此过滤器添加到UsernamePasswordAuthenticationFilter之前。

javaBean public SecurityFilterChain securityFilterChain(HttpSecurity http, JwtAuthenticationTokenFilter jwtFilter) throws Exception { http // ... 其他配置 .addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class); return http.build(); }

3 JWT 续期策略JWT有效期过期后需要重新获取。

有两种常见续期方案方案一Refresh Token 机制Access Token: 短期令牌如2小时用于访问资源。

Refresh Token: 长期令牌如7天用于获取新的Access Token存储在服务端或安全的客户端存储中。

javapublic String refreshAccessToken(String refreshToken) { //

校验Refresh Token的有效性签名、过期时间等 boolean isValid validateRefreshToken(refreshToken); if (!isValid) { throw new RuntimeException(无效的Refresh Token); } //

从Refresh Token中获取用户信息 String userId getUserIdFromRefreshToken(refreshToken); //

生成新的Access Token String newAccessToken generateAccessToken(userId); return newAccessToken; }方案二自动延长有效期滑动过期每次有效请求后都重新颁发一个全新的JWT重置过期时间。

这种方式更简单但会增加令牌的频繁签发。

javapublic String getAccessToken(HttpServletRequest request) { String oldToken extractTokenFromRequest(request); if (isTokenExpired(oldToken)) { // 已过期需重新登录 throw new RuntimeException(Token expired); } else if (shouldRefreshToken(oldToken)) { // 即将过期如剩余时间5分钟重新签发 String userId extractUserIdFromToken(oldToken); return generateNewAccessToken(userId); } return oldToken; }

总结Spring Security 6 提供了一个强大、灵活且深度集成于Spring生态的安全框架。

通过本文我们从认证和授权两大核心功能入手逐步掌握了从基础配置、自定义用户来源、前后端分离适配到与JWT整合实现无状态API防护的全流程。

核心要点回顾认证是基础理解UserDetailsService和PasswordEncoder的角色。

授权是关键灵活运用URL匹配和方法注解控制访问权限。

整合是趋势在微服务和前后端分离架构下采用Spring Security JWT是构建安全API网关和服务的优选方案。

安全无小事务必妥善管理密钥JWT签名密钥并合理设计Token的过期与刷新机制。

XXXXXL19D18每19-XXXXXL19D18每应用

百度百家号客服电话人工服务

123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123 123