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

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

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

在这里插入图片描述

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 进行身份验证和授权。

猜你喜欢

转载自blog.csdn.net/JasonXu94/article/details/131353326