深入浅出:基于SpringBoot和JWT的后端鉴权系统设计与实现

文章目录

    • 什么是鉴权系统
      • 定义与作用
      • 主要组成部分
      • 工作原理
      • 常用技术和框架
    • 基于SpringBoot + JWT的鉴权系统设计与实现指南
      • 前言
      • 技术对比
      • 令牌技术
      • JWT令牌
      • 实现全流程
        • 1. **依赖引入**
        • 2. **JWT 工具类**
        • 3. **JWT 拦截器(Interceptor)**
      • 4. **拦截器注册**
      • 5. **登录接口**

什么是鉴权系统

后端开发鉴权系统是一种用于验证和授权用户访问后端资源的系统,在保障系统安全和资源合理访问方面起着关键作用,以下是关于它的详细介绍:

定义与作用

  • 定义:鉴权系统主要用于判断用户是否具有访问特定资源或执行特定操作的权限。它通过一系列的规则和流程,对用户的身份和权限进行验证和管理,确保只有经过授权的用户才能访问相应的资源或执行相应的操作。
  • 作用:鉴权系统能有效保护后端数据和功能的安全,防止未经授权的访问、数据泄露、恶意操作等安全威胁,保证系统的稳定性和数据的完整性。

主要组成部分

  • 用户管理模块:负责用户信息的存储和管理,包括用户的基本信息、账号密码、角色等。
  • 认证模块:主要用于验证用户的身份,常见的认证方式有用户名 / 密码认证、令牌认证、OAuth 认证等。
  • 授权模块:根据用户的身份和角色,确定用户对不同资源的访问权限。通常通过权限列表、访问控制列表(ACL)、角色基于访问控制(RBAC)等技术实现。
  • 令牌生成与管理模块:在一些认证方式中,会生成令牌用于用户后续的访问验证。该模块负责令牌的生成、颁发、刷新和验证等操作。
  • 数据存储:用于存储用户信息、权限信息、令牌等数据,常见的存储方式有数据库、缓存等。

工作原理

  1. 用户认证:用户首先向系统提交身份凭证,如用户名和密码。认证模块接收到用户提交的信息后,对其进行验证。如果验证成功,系统会为用户生成一个唯一的身份标识,如令牌。
  2. 权限检查:用户在访问受保护的资源或执行特定操作时,系统会根据用户的身份标识,检查其是否具有相应的权限。授权模块会查询权限配置信息,判断用户是否被允许访问该资源或执行该操作。
  3. 访问控制:根据权限检查的结果,系统决定是否允许用户访问资源或执行操作。如果用户具有相应权限,系统将允许用户访问资源或执行操作,并返回相应的结果;如果用户没有权限,系统将拒绝用户的请求,并返回相应的错误信息。

常用技术和框架

  • Spring Security:是一个针对 Spring 框架的安全框架,提供了全面的安全解决方案,包括认证、授权、加密等功能。
  • Apache Shiro:是一个功能强大且灵活的 Java 安全框架,提供了认证、授权、加密、会话管理等功能。
  • JWT(JSON Web Tokens):是一种用于在网络应用间传递声明的开放标准,常用于用户认证和授权。它可以将用户信息编码为一个 JSON 对象,并通过签名来保证数据的完整性和安全性。

基于SpringBoot + JWT的鉴权系统设计与实现指南

前言

现代Web应用的鉴权挑战在微服务架构盛行的当下,传统Session鉴权机制面临扩展性差、跨域支持复杂等问题。下面将以SpringBoot为基础框架,详细讲解如何通过JWT(JSON Web Token)构建安全高效的鉴权系统,涵盖核心原理、完整实现路径及安全最佳实践。

技术对比

特性 Session-Cookie JWT
状态管理 服务端存储 无状态
扩展性 集群需同步 天然支持分布式
跨域支持 需额外配置 原生支持CORS
移动端适配 Cookie处理复杂 Header携带更友好

令牌技术

实现

  • 本质是一个字符串
  • 登录成功,服务器生成一个令牌作为该用户的合法身份凭证,在响应数据中把令牌传递给前端
  • 前端将令牌存储(可以存在Cookie,也可以存在其他空间中)
    优点
  • 支持PC、移动端
  • 解决集群环境下的认证问题
  • 减轻服务器存储压力

JWT令牌

JSON Web Token Introduction - jwt.io

  • 全称:JSON Web Token
  • 功能:定义了一种简洁的、自包含的格式,用于在通信双方以json数据格式安全的传输信息。由于数字签名的存在,这些信息是可靠的。
  • 组成
    • 第一部分:Header(头),记录令牌类型,签名算法等,例如:{“alg”:"“HS256”,“type”:“JWT”}
    • 第二部分:PayLoad(有效载荷),携带一些自定义信息,默认信息等。例如:{“id”:“1”,“username”:“Tom”}
    • 第三部分:Signature(签名),防止Token被篡改,确保安全性。用header、payload,并加入指定秘钥,通过指定签名算法计算而来。

Base64: 是一种基于64个可打印字符(A-Z a-z 0-9 + = /)来表示二进制数据的编码方式

实现全流程

1. 依赖引入

首先,确保你的 pom.xml 中包含了 Spring Boot Web 和 JWT 的相关依赖:

<dependencies>
    
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-webartifactId>
    dependency>

    
    <dependency>
        <groupId>io.jsonwebtokengroupId>
        <artifactId>jjwt-apiartifactId>
        <version>0.11.5version>
    dependency>
dependencies>

2. JWT 工具类

这个工具类包含了生成和解析 JWT 的方法。我们会使用一个密钥来签名 Token,并支持过期时间的设置。

import io.jsonwebtoken.*;

import java.util.Date;
import java.util.List;
import java.util.Base64;

public class JwtUtils {

    // 密钥,用于加密和解密JWT
    private static final String SECRET_KEY = Base64.getEncoder().encodeToString("your-256bit-secret".getBytes());
    // Token有效期(1小时)
    private static final long EXPIRATION_MS = 3600000;

    // 生成Token
    public static String generateToken(String username, List<String> roles) {
        return Jwts.builder()
                .setSubject(username)  // 设置用户名
                .claim("roles", roles)  // 添加自定义主体信息
                .setIssuedAt(new Date())  // 设置发行时间
                .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_MS))  // 设置过期时间
                .signWith(SignatureAlgorithm.HS256, SECRET_KEY)  // 使用HS256加密算法和密钥签名
                .compact();  // 返回JWT
    }

    // 解析Token
    public static Claims parseToken(String token) {
        try {
            return Jwts.parser()
                    .setSigningKey(SECRET_KEY)  // 设置密钥进行解密
                    .parseClaimsJws(token)  // 解析JWT
                    .getBody();  // 返回Token的主体部分(Claims)
        } catch (ExpiredJwtException e) {
            throw new RuntimeException("Token已过期");
        } catch (Exception e) {
            throw new RuntimeException("无效的Token");
        }
    }
}
3. JWT 拦截器(Interceptor)

我们需要一个拦截器来验证请求中的 JWT Token。所有需要认证的请求都会经过这个拦截器检查。

import io.jsonwebtoken.Claims;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class JwtInterceptor implements HandlerInterceptor {

    // 处理请求前,检查Authorization头中的Token
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 获取Authorization头
        String token = request.getHeader("Authorization");

        if (token == null || !token.startsWith("Bearer ")) {
            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);  // 返回401
            response.getWriter().write("缺少有效的认证凭证");
            return false;
        }

        try {
            // 去掉 "Bearer " 前缀,获取实际的Token
            Claims claims = JwtUtils.parseToken(token.substring(7));  // 解析Token
            // 将解析后的用户信息放到request的属性中,以便后续使用
            request.setAttribute("userClaims", claims);
        } catch (Exception e) {
            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);  // 返回401
            response.getWriter().write("Token无效或过期");
            return false;
        }

        return true;  // 继续执行下一个拦截器或控制器
    }
}

4. 拦截器注册

接下来,我们需要将拦截器注册到 Spring MVC 配置中。

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 将JwtInterceptor添加到拦截器链中
        registry.addInterceptor(new JwtInterceptor())
                .addPathPatterns("/protected/**")  // 只拦截需要认证的接口(例如/protected/**)
                .excludePathPatterns("/login", "/register");  // 不拦截登录和注册等无需认证的接口
    }
}

5. 登录接口

创建一个简单的登录接口,成功登录后生成 JWT 返回给客户端。

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.util.Arrays;

@RestController
public class AuthController {

    @PostMapping("/login")
    public ResponseEntity<?> login(@RequestBody LoginRequest request) {
        // 假设用户名和密码验证成功
        String username = request.getUsername();
        String password = request.getPassword();

        // 生成JWT Token
        String token = JwtUtils.generateToken(username, Arrays.asList("ROLE_USER"));

        // 返回JWT
        return ResponseEntity.ok(new AuthResponse(token));
    }
}

你可能感兴趣的:(spring,boot,后端,java)