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

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
使用Spring BootJwt进行身份验证可以保护您的应用程序免受未经授权的访问。下面是使用Spring BootJwt进行身份验证的步骤: 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> ``` 2. 创建JwtUtils类 创建一个 `JwtUtils` 类,用于生成和解析Jwt令牌。以下是一个示例实现: ```java import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import org.springframework.beans.factory.annotation.Value; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.stereotype.Component; import java.util.Date; import java.util.HashMap; import java.util.Map; import java.util.function.Function; @Component public class JwtUtils { @Value("${jwt.secret}") private String secret; @Value("${jwt.expiration}") private Long expiration; public String generateToken(UserDetails userDetails) { Map<String, Object> claims = new HashMap<>(); return createToken(claims, userDetails.getUsername()); } private String createToken(Map<String, Object> claims, String subject) { Date now = new Date(); Date expirationDate = new Date(now.getTime() + expiration); return Jwts.builder() .setClaims(claims) .setSubject(subject) .setIssuedAt(now) .setExpiration(expirationDate) .signWith(SignatureAlgorithm.HS256, secret) .compact(); } public boolean validateToken(String token, UserDetails userDetails) { final String username = extractUsername(token); return (username.equals(userDetails.getUsername()) && !isTokenExpired(token)); } public boolean isTokenExpired(String token) { final Date expirationDate = extractExpiration(token); return expirationDate.before(new Date()); } public String extractUsername(String token) { return extractClaim(token, Claims::getSubject); } public Date extractExpiration(String token) { return extractClaim(token, Claims::getExpiration); } public <T> T extractClaim(String token, Function<Claims, T> claimsResolver) { final Claims claims = extractAllClaims(token); return claimsResolver.apply(claims); } private Claims extractAllClaims(String token) { return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody(); } } ``` 3. 创建JwtRequestFilter 创建一个 `JwtRequestFilter` 类,用于拦截所有安全请求并验证Jwt令牌。以下是一个示例实现: ```java import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; import org.springframework.stereotype.Component; 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; @Component public class JwtRequestFilter extends OncePerRequestFilter { @Autowired private UserDetailsService userDetailsService; @Autowired private JwtUtils jwtUtils; @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { final String authorizationHeader = request.getHeader("Authorization"); String username = null; String jwt = null; if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) { jwt = authorizationHeader.substring(7); username = jwtUtils.extractUsername(jwt); } if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) { UserDetails userDetails = this.userDetailsService.loadUserByUsername(username); if (jwtUtils.validateToken(jwt, userDetails)) { UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = new UsernamePasswordAuthenticationToken( userDetails, null, userDetails.getAuthorities()); usernamePasswordAuthenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken); } } filterChain.doFilter(request, response); } } ``` 4. 创建JwtAuthenticationController 创建一个 `JwtAuthenticationController` 类,用于处理用户身份验证请求并返回Jwt令牌。以下是一个示例实现: ```java import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.ResponseEntity; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.web.bind.annotation.*; @RestController public class JwtAuthenticationController { @Autowired private AuthenticationManager authenticationManager; @Autowired private UserDetailsService userDetailsService; @Autowired private JwtUtils jwtUtils; @Autowired private PasswordEncoder passwordEncoder; @PostMapping("/authenticate") public ResponseEntity<?> createAuthenticationToken(@RequestBody JwtRequest jwtRequest) throws Exception { try { authenticationManager.authenticate( new UsernamePasswordAuthenticationToken(jwtRequest.getUsername(), jwtRequest.getPassword())); } catch (Exception e) { throw new Exception("Incorrect username or password", e); } final UserDetails userDetails = userDetailsService.loadUserByUsername(jwtRequest.getUsername()); final String token = jwtUtils.generateToken(userDetails); return ResponseEntity.ok(new JwtResponse(token)); } @PostMapping("/register") public ResponseEntity<?> saveUser(@RequestBody UserDto userDto) { User user = new User(userDto.getUsername(), passwordEncoder.encode(userDto.getPassword())); userDetailsService.save(user); return ResponseEntity.ok(user); } } ``` 5. 配置WebSecurityConfigurerAdapter 最后,我们需要配置WebSecurityConfigurerAdapter以启用基于Jwt的身份验证。以下是一个示例实现: ```java import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; 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.core.userdetails.UserDetailsService; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; @Configuration @EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private JwtRequestFilter jwtRequestFilter; @Autowired private UserDetailsService userDetailsService; @Autowired private PasswordEncoder passwordEncoder; @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder); } @Override protected void configure(HttpSecurity http) throws Exception { http.csrf().disable() .authorizeRequests().antMatchers("/authenticate", "/register").permitAll() .anyRequest().authenticated() .and().sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS); http.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class); } @Bean @Override public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } } ``` 现在,您的Spring Boot应用程序已经配置使用Jwt进行身份验证。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员徐师兄

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值