Spring Boot 如何使用 JWT 进行认证和授权

简介: Spring Boot 如何使用 JWT 进行认证和授权

Spring Boot 如何使用 JWT 进行认证和授权


JSON Web Token(JWT)是一种用于安全地传输信息的开放标准。JWT 是一种轻量级的身份验证和授权机制,可以在客户端和服务器之间安全地传输信息。在本文中,我们将介绍如何在 Spring Boot 应用程序中使用 JWT 进行认证和授权。


7c895a956bdaa171a48a52c351952e23_646274175db34aa587ca0a9101c1acbc.png


JWT 概述


JWT 是一种基于 JSON 的开放标准,用于在客户端和服务器之间安全地传输信息。JWT 由三部分组成:Header、Payload 和 Signature。


Header


Header 通常由两部分组成:token 类型和算法。例如,以下是一个 JWT Header 的示例:


{
  "alg": "HS256",
  "typ": "JWT"
}


在上面的示例中,alg 表示使用的算法,typ 表示 token 的类型。


Payload


Payload 包含要传输的信息。Payload 通常由一些标准字段和自定义字段组成。例如,以下是一个 JWT Payload 的示例:


{
  "sub": "1234567890",
  "name": "John Doe",
  "iat": 1516239022
}


在上面的示例中,sub 表示主题,name 表示名称,iat 表示 token 的创建时间。


Signature


Signature 用于验证 token 的真实性和完整性。Signature 由 Header、Payload 和一个密钥组成,可以使用算法对它们进行签名。例如,以下是一个 JWT Signature 的示例:


HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  secret)


在上面的示例中,secret 表示密钥。


JWT 认证和授权


在 Spring Boot 应用程序中,我们可以使用 JWT 实现身份验证和授权。通常,我们需要实现以下步骤:


  1. 客户端通过用户名和密码进行身份验证。
  2. 服务器生成 JWT 并将其发送给客户端。
  3. 客户端将 JWT 存储在本地。
  4. 客户端向服务器发送请求时,在请求头中添加 JWT。
  5. 服务器验证 JWT,并根据 JWT 中的信息授权。


下面是一个示例 Spring Boot 应用程序,用于实现 JWT 认证和授权。


添加依赖

首先,我们需要在 pom.xml 文件中添加以下依赖:


<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-api</artifactId>
    <version>0.11.2</version>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-impl</artifactId>
    <version>0.11.2</version>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-jackson</artifactId>
    <version>0.11.2</version>
    <scope>runtime</scope>
</dependency>


创建实体类


我们需要创建一个 User 类来表示用户信息。例如,以下是一个 User 类的示例:


public class User {
    private String username;
    private String password;
    public User() {}
    public User(String username, String password) {
        this.username = username;
        this.password = password;
    }
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
}


创建认证接口


我们需要创建一个认证接口来处理用户身份验证请求。例如,以下是一个 AuthController 类的示例:


@RestController
@RequestMapping("/auth")
public class AuthController {
    private final AuthenticationManager authenticationManager;
    private final JwtUtils jwtUtils;
    public AuthController(AuthenticationManager authenticationManager, JwtUtils jwtUtils) {
        this.authenticationManager = authenticationManager        ;
        this.jwtUtils = jwtUtils;
    }
    @PostMapping("/login")
    public ResponseEntity<?> authenticateUser(@RequestBody User user) {
        Authentication authentication = authenticationManager.authenticate(
                new UsernamePasswordAuthenticationToken(user.getUsername(), user.getPassword()));
        SecurityContextHolder.getContext().setAuthentication(authentication);
        String jwt = jwtUtils.generateJwtToken(authentication);
        return ResponseEntity.ok(new JwtResponse(jwt));
    }
}

在上面的示例中,我们使用 @RestController 和 @RequestMapping 注解创建了一个名为 AuthController 的类。我们使用 @PostMapping 和 @RequestBody 注解创建了一个名为 authenticateUser 的方法,用于处理用户身份验证请求。在该方法中,我们使用 AuthenticationManager 对象进行身份验证,并使用 JwtUtils 对象生成 JWT。


创建 JWT 工具类


我们需要创建一个 JwtUtils 类来生成和验证 JWT。例如,以下是一个 JwtUtils 类的示例:


@Component
public class JwtUtils {
    @Value("${jwt.secret}")
    private String secret;
    @Value("${jwt.expiration}")
    private int expiration;
    public String generateJwtToken(Authentication authentication) {
        UserDetailsImpl userPrincipal = (UserDetailsImpl) authentication.getPrincipal();
        return Jwts.builder()
                .setSubject((userPrincipal.getUsername()))
                .setIssuedAt(new Date())
                .setExpiration(new Date((new Date()).getTime() + expiration * 1000))
                .signWith(SignatureAlgorithm.HS512, secret)
                .compact();
    }
    public boolean validateJwtToken(String authToken) {
        try {
            Jwts.parser().setSigningKey(secret).parseClaimsJws(authToken);
            return true;
        } catch (SignatureException e) {
            logger.error("Invalid JWT signature: {}", e.getMessage());
        } catch (MalformedJwtException e) {
            logger.error("Invalid JWT token: {}", e.getMessage());
        } catch (ExpiredJwtException e) {
            logger.error("JWT token is expired: {}", e.getMessage());
        } catch (UnsupportedJwtException e) {
            logger.error("JWT token is unsupported: {}", e.getMessage());
        } catch (IllegalArgumentException e) {
            logger.error("JWT claims string is empty: {}", e.getMessage());
        }
        return false;
    }
    public String getUsernameFromJwtToken(String token) {
        return Jwts.parser()
                .setSigningKey(secret)
                .parseClaimsJws(token)
                .getBody().getSubject();
    }
}

在上面的示例中,我们使用 @Component 注解创建了一个名为 JwtUtils 的类。我们使用 @Value 注解获取 JWT 密钥和过期时间。我们使用 Jwts.builder() 方法创建一个 JWT,并使用 setSubject()、setIssuedAt() 和 setExpiration() 方法设置 JWT 的主题、创建时间和过期时间。我们使用 signWith() 方法和 HS512 算法对 JWT 进行签名,并使用 compact() 方法生成 JWT。我们使用 Jwts.parser() 方法验证 JWT,并使用 parseClaimsJws() 方法获取 JWT 中的主题。我们使用各种异常处理机制来处理 JWT 验证过程中可能出现的异常。


创建授权拦截器


我们需要创建一个授权拦截器来验证请求中的 JWT。例如,以下是一个 JwtAuthTokenFilter 类的示例:


public class JwtAuthTokenFilter extends OncePerRequestFilter {
    private final JwtUtils jwtUtils;
    public JwtAuthTokenFilter(JwtUtils jwtUtils) {
        this.jwtUtils = jwtUtils;
    }
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        try {
            String jwt = parseJwt(request);
            if (jwt != null && jwtUtils.validateJwtToken(jwt)) {
                String username = jwtUtils.getUsernameFromJwtToken(jwt);
                UserDetails userDetails = userDetailsService.loadUserByUsername(username);
                UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
                        userDetails, null, userDetails.getAuthorities());
                authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                SecurityContextHolder.getContext().setAuthentication(authentication);
            }
        } catch (Exception e) {
            logger.error("Cannot set user authentication: {}", e.getMessage());
        }
        filterChain.doFilter(request, response);
    }
    private String parseJwt(HttpServletRequest request) {
        String headerAuth = request.getHeader("Authorization");
        if (StringUtils.hasText(headerAuth) && headerAuth.startsWith("Bearer ")) {
            return headerAuth.substring(7, headerAuth.length());
        }
        return null;
    }
}

在上面的示例中,我们使用 OncePerRequestFilter 类创建了一个名为 JwtAuthTokenFilter 的拦截器。在 doFilterInternal() 方法中,我们使用 parseJwt() 方法获取请求头中的 JWT,并使用 JwtUtils 类中的 validateJwtToken() 方法验证 JWT。如果 JWT 验证成功,我们使用 getUsernameFromJwtToken() 方法获取 JWT 中的用户名,并使用 userDetailsService 加载用户详细信息。我们使用 UsernamePasswordAuthenticationToken 对象创建一个新的身份验证对象,并使用 SecurityContextHolder 将其设置为当前身份验证对象。最后,我们调用 filterChain.doFilter() 方法将请求传递给下一个过滤器。


配置 Spring Security


我们需要在 Spring Security 配置中添加 JwtAuthTokenFilter 来验证 JWT。例如,以下是一个 SecurityConfiguration 类的示例:


@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
    @Autowired
    private UserDetailsServiceImpl userDetailsService;
    @Autowired
    private JwtUtils jwtUtils;
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable().authorizeRequests()
                .antMatchers("/auth/**").permitAll()
                .anyRequest().authenticated()
                .and()
                .addFilterBefore(new JwtAuthTokenFilter(jwtUtils), UsernamePasswordAuthenticationFilter.class);
    }
    @Autowired
    public void configureAuthentication(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception {
        authenticationManagerBuilder.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
    }
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }
}

在上面的示例中,我们使用 @Configuration 和 @EnableWebSecurity 注解创建了一个名为 SecurityConfiguration 的类。我们使用 @Autowired 注解注入了一个名为 userDetailsService 的 UserDetailsServiceImpl 对象和一个名为 jwtUtils 的 JwtUtils 对象。我们使用 configure() 方法配置了 HTTP 安全性,并使用 antMatchers() 方法指定不需要身份验证的 URL。我们使用 addFilterBefore() 方法添加了 JwtAuthTokenFilter 来验证 JWT。我们使用 configureAuthentication() 方法配置了身份验证管理器,并使用 passwordEncoder() 方法创建了一个密码编码器。我们使用 authenticationManagerBean() 方法创建了一个身份验证管理器。


创建响应类


最后,我们需要创建一个 JwtResponse 类来表示 JWT 响应。例如,以下是一个 JwtResponse 类的示例:


public class JwtResponse {
    private final String jwt;
    public JwtResponse(String jwt) {
        this.jwt = jwt;
    }
    public String getJwt() {
        return jwt;
    }
}


结论


在本文中,我们介绍了如何在 Spring Boot 应用程序中使用 JWT 进行身份验证和授权。我们创建了一个 User 类来表示用户信息,创建了一个 AuthController 类来处理用户身份验证请求,创建了一个 JwtUtils 类来生成和验证 JWT,创建了一个 JwtAuthTokenFilter 类来验证请求中的 JWT,创建了一个 SecurityConfiguration 类来配置 Spring Security,创建了一个 JwtResponse 类来表示 JWT 响应。我们希望本文能够帮助您理解如何在 Spring Boot 应用程序中使用 JWT 进行身份验证和授权。


相关文章
1天搞定SpringBoot+Vue全栈开发 (9)JWT跨域认证
1天搞定SpringBoot+Vue全栈开发 (9)JWT跨域认证
|
19天前
|
JSON SpringCloudAlibaba Cloud Native
SpringCloudAlibaba:4.3云原生网关higress的JWT 认证
SpringCloudAlibaba:4.3云原生网关higress的JWT 认证
19 1
|
19天前
|
存储 JSON 算法
SpringBoot之JWT令牌校验
SpringBoot之JWT令牌校验
35 2
|
19天前
|
运维 Serverless API
Serverless 应用引擎产品使用之阿里函数计算中要关掉http触发器的jwt认证才可以进行性能探测如何解决
阿里云Serverless 应用引擎(SAE)提供了完整的微服务应用生命周期管理能力,包括应用部署、服务治理、开发运维、资源管理等功能,并通过扩展功能支持多环境管理、API Gateway、事件驱动等高级应用场景,帮助企业快速构建、部署、运维和扩展微服务架构,实现Serverless化的应用部署与运维模式。以下是对SAE产品使用合集的概述,包括应用管理、服务治理、开发运维、资源管理等方面。
|
19天前
|
JSON 安全 API
【专栏】四种REST API身份验证方法:基本认证、OAuth、JSON Web Token(JWT)和API密钥
【4月更文挑战第28天】本文探讨了四种REST API身份验证方法:基本认证、OAuth、JSON Web Token(JWT)和API密钥。基本认证简单但不安全;OAuth适用于授权第三方应用;JWT提供安全的身份验证信息传递;API密钥适合内部使用。选择方法时需平衡安全性、用户体验和开发复杂性。
|
19天前
|
SQL 安全 Java
微服务之Springboot整合Oauth2.0 + JWT
微服务之Springboot整合Oauth2.0 + JWT
23 1
|
19天前
|
安全 Java UED
第5章 Spring Security 的高级认证技术(2024 最新版)(下)
第5章 Spring Security 的高级认证技术(2024 最新版)
29 0
|
19天前
|
安全 Java API
第5章 Spring Security 的高级认证技术(2024 最新版)(上)
第5章 Spring Security 的高级认证技术(2024 最新版)
57 0
|
设计模式 前端开发 Java
基于Springboot实现专业认证材料管理系统
该知识产权服务平台系统项目采用mvc设计模式, 其中知识产权服务平台系统的视图与知识产权服务平台系统业务逻辑进行了分层设计, 特别方便后续知识产权服务平台系统系统的开发 设计这种mvc的架构的好处是完全的可以将业务进行分层, 进行高内聚低耦合, 分为service层, dao层, controller层, 架构清晰 本项目主要基于Springboot 和ruoyi来开发一套专业认证材料管理系统,对各专业相关的文档材料进行管理,主要包含的功能模块有: 系统管理:用户管理、角色管理、菜单管理、操作日志 业务模块:专业管理、认证材料管理、相关网站管理
160 0
基于Springboot实现专业认证材料管理系统
|
2天前
|
JavaScript Java 测试技术
基于SpringBoot+Vue+uniapp的漫画阅读系统的详细设计和实现(源码+lw+部署文档+讲解等)
基于SpringBoot+Vue+uniapp的漫画阅读系统的详细设计和实现(源码+lw+部署文档+讲解等)