SpringSecurity-前后端分离

在前后端分离的架构中,Spring Security 的配置与传统的单体应用有所不同。为了确保安全性和灵活性,我们需要对 Spring Security 进行适当的调整以适应这种架构。下面将详细介绍如何在前后端分离的应用程序中实现 Spring Security。

1. 理解前后端分离的安全需求

在前后端分离的应用程序中,前端通常是一个独立的 Web 应用或移动应用,而后端提供 RESTful API 或 GraphQL 接口。因此,我们需要考虑以下几点:

  • 认证:用户登录后,前端应该获得一个令牌(如 JWT),并在后续请求中携带此令牌来证明身份。
  • 授权:根据用户的权限级别,控制他们可以访问哪些资源。
  • 跨域资源共享 (CORS):由于前端和后端可能部署在不同的域名上,需要处理 CORS 请求。
  • 会话管理:避免使用基于 Cookie 的会话机制,转而采用无状态的身份验证方式。

2. 选择认证机制

对于前后端分离的应用,推荐使用 JSON Web Token (JWT)OAuth2 来进行认证。这里我们主要讨论 JWT 的实现。

2.1 JSON Web Token (JWT)

JWT 是一种自包含的令牌格式,它允许我们在客户端存储用户信息,并且可以在每次 HTTP 请求时通过 Authorization Header 发送到服务器。JWT 包含三个部分:Header、Payload 和 Signature。

  • 优点

    • 无状态:服务器不需要存储会话信息,减轻了服务器负担。
    • 跨域友好:易于在不同域名之间传递。
    • 简单易用:前端可以直接保存到 localStorage 或 sessionStorage 中。
  • 缺点

    • 安全性依赖于密钥管理:如果私钥泄露,所有签发的 JWT 都可能被伪造。
    • 不适合频繁更改权限场景:因为 JWT 是自签名的,一旦生成就难以撤销。

3. 配置 Spring Security

接下来我们将介绍如何配置 Spring Security 来支持 JWT 认证。

3.1 添加依赖

首先,在 pom.xml 文件中添加必要的依赖项:

XML 复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.1</version>
</dependency>
3.2 创建 JWT 工具类

创建一个工具类用于生成和解析 JWT:

java 复制代码
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

public class JwtUtil {

    private static final String SECRET = "your-secret-key"; // 私钥,请确保足够复杂

    public static String generateToken(String username) {
        return Jwts.builder()
                .setSubject(username)
                .signWith(SignatureAlgorithm.HS512, SECRET.getBytes())
                .compact();
    }

    public static Claims parseToken(String token) {
        return Jwts.parser()
                .setSigningKey(SECRET.getBytes())
                .parseClaimsJws(token)
                .getBody();
    }
}
3.3 自定义过滤器

编写一个自定义过滤器来拦截每个请求并验证 JWT:

java 复制代码
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class JwtAuthenticationFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {

        String header = request.getHeader("Authorization");

        if (header != null && header.startsWith("Bearer ")) {
            try {
                String jwt = header.substring(7);
                Claims claims = JwtUtil.parseToken(jwt);

                // 设置当前线程的安全上下文
                SecurityContextHolder.getContext().setAuthentication(new UsernamePasswordAuthenticationToken(
                        claims.getSubject(), null, Collections.emptyList()));
            } catch (Exception e) {
                logger.warn("Failed to set user authentication: {}", e.getMessage());
            }
        }

        filterChain.doFilter(request, response);
    }
}
3.4 配置 Spring Security

最后,配置 Spring Security 以集成 JWT 和 CORS 支持:

java 复制代码
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.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .cors().and() // 启用 CORS 支持
            .csrf().disable() // 禁用 CSRF 保护(因为我们使用 JWT)
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) // 禁用会话
            .and()
            .authorizeRequests()
                .antMatchers("/auth/**").permitAll() // 允许未认证用户访问认证接口
                .anyRequest().authenticated(); // 所有其他请求都需要认证

        // 将自定义过滤器添加到过滤器链中
        http.addFilterBefore(new JwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
    }

    @Bean
    public CorsFilter corsFilter() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration config = new CorsConfiguration();
        config.setAllowCredentials(true);
        config.addAllowedOrigin("*");
        config.addAllowedHeader("*");
        config.addAllowedMethod("*");
        source.registerCorsConfiguration("/**", config);
        return new CorsFilter(source);
    }
}

4. 前端实现

在前端部分,你需要:

  • 在用户登录成功后保存返回的 JWT 到本地存储(如 localStoragesessionStorage)。
  • 在每次发送请求时,在 Authorization 头部添加 Bearer <token>
  • 当遇到 401 或 403 错误时,重定向至登录页面或显示适当的消息提示。
相关推荐
e***98571 天前
SpringMVC的工作流程
状态模式
q***08741 天前
SpringMVC的工作流程
状态模式
g***78912 天前
SpringBoot中使用TraceId进行日志追踪
spring boot·后端·状态模式
shuxiaohua4 天前
使用HttpURLConnection调用SSE采坑记录
状态模式
崎岖Qiu4 天前
状态模式与策略模式的快速区分与应用
笔记·设计模式·状态模式·策略模式·开闭原则
Jonathan Star4 天前
前端需要做单元测试吗?哪些适合做?
前端·单元测试·状态模式
一水鉴天6 天前
整体设计 全面梳理复盘 之40 M3 统摄三层 AI 的动态运营社区(Homepage)设计
架构·transformer·状态模式·公共逻辑
前端玖耀里8 天前
Vue + Axios + Node.js(Express)如何实现无感刷新Token?
状态模式
将编程培养成爱好9 天前
C++ 设计模式《外卖骑手状态系统》
c++·ui·设计模式·状态模式
向葭奔赴♡10 天前
Spring Boot参数校验全流程解析
状态模式