Spring Boot整合Spring Security和JWT

编程语言译者 2024-06-02 ⋅ 23 阅读

简介

在现代Web应用中,安全性是至关重要的。Spring Security是一个功能强大且广泛使用的Java安全框架,可以帮助我们轻松地添加身份验证和授权功能。而JWT(JSON Web Token)是一种轻量级且安全的身份验证机制,可以在客户端和服务器之间传递信息。这篇博客将介绍如何使用Spring Boot整合Spring Security和JWT,以提供更安全的Web应用。

环境配置

首先,我们需要创建一个Spring Boot项目。可以通过Spring Initializer进行初始化,也可以手动创建一个空的Maven项目,并将以下依赖添加到pom.xml中:

<dependencies>
    <!-- Spring Boot -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <!-- Spring Security -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>

    <!-- JSON Web Token -->
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt</artifactId>
        <version>0.9.1</version>
    </dependency>
</dependencies>

添加Spring Security配置

我们需要创建一个SecurityConfig类,用于配置Spring Security组件。该类需要继承自WebSecurityConfigurerAdapter,并重写configure方法:

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
                .authorizeRequests()
                .antMatchers("/api/public/**").permitAll() // 公开访问的API
                .anyRequest().authenticated() // 其他API需要身份验证
                .and()
                .httpBasic(); // 使用基本身份验证
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        // 在内存中创建一个用户
        auth.inMemoryAuthentication()
                .withUser("admin")
                .password("{noop}password") // 密码加密,使用明文密码
                .roles("USER");
    }
}

配置中我们定义了两个API,/api/public/**是公开访问的,其他API需要身份验证。这里我们使用了基本身份验证,并在内存中创建了一个用户名为admin密码为password的用户。

添加JWT配置

为了使用JWT,在之前创建的SecurityConfig类中添加以下方法:

@Bean
public JwtAuthenticationFilter jwtAuthenticationFilter() {
    return new JwtAuthenticationFilter();
}

@Override
protected void configure(HttpSecurity http) throws Exception {
    // ...
    http.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
}

我们创建了一个名为jwtAuthenticationFilterJwtAuthenticationFilter的Bean,并将其添加到Spring Security过滤器链中。

接下来,我们需要创建JwtUtils类,用于生成和验证JWT:

public class JwtUtils {

    private static final String SECRET_KEY = "secretkey"; // 密钥
    private static final long EXPIRATION_TIME = 864_000_000; // 过期时间: 10天

    public static String generateToken(String username) {
        Date now = new Date();
        Date expiryDate = new Date(now.getTime() + EXPIRATION_TIME);

        return Jwts.builder()
                .setSubject(username)
                .setIssuedAt(now)
                .setExpiration(expiryDate)
                .signWith(SignatureAlgorithm.HS512, SECRET_KEY)
                .compact();
    }

    public static String getUsernameFromToken(String token) {
        return Jwts.parser()
                .setSigningKey(SECRET_KEY)
                .parseClaimsJws(token)
                .getBody()
                .getSubject();
    }

    public static boolean validateToken(String token) {
        try {
            Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token);
            return true;
        } catch (SignatureException ex) {
            return false;
        }
    }
}

我们定义了一个密钥和过期时间,并提供了生成、解析和验证JWT的方法。

创建JWT身份验证过滤器

现在,我们创建一个名为JwtAuthenticationFilter的过滤器类,用于验证请求中的JWT。当用户进行身份验证时,我们将生成一个JWT,并将其添加到响应头中。当用户发送带有JWT的请求时,我们将解析JWT,并使用Spring Security进行身份验证。

public class JwtAuthenticationFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        String jwt = getJwtFromRequest(request);

        if (StringUtils.hasText(jwt) && JwtUtils.validateToken(jwt)) {
            String username = JwtUtils.getUsernameFromToken(jwt);

            UserDetails userDetails = userDetailsService.loadUserByUsername(username);
            UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
            authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));

            SecurityContextHolder.getContext().setAuthentication(authentication);
        }

        filterChain.doFilter(request, response);
    }

    private String getJwtFromRequest(HttpServletRequest request) {
        String bearerToken = request.getHeader("Authorization");
        if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
            return bearerToken.substring(7);
        }
        return null;
    }
}

该过滤器从请求头中获取JWT,并进行验证。如果JWT有效,则获取用户名,并使用UserDetailsService加载用户详细信息。然后,我们将身份验证令牌设置到SecurityContextHolder中,以便后续授权使用。

添加登录API

接下来,我们需要创建一个登录API,用于生成JWT并返回给客户端。

@RestController
@RequestMapping("/api/public")
public class AuthenticationController {

    @PostMapping("/login")
    public ResponseEntity<?> authenticateUser(@RequestBody LoginRequest request) {
        Authentication authentication = authenticationManager.authenticate(
                new UsernamePasswordAuthenticationToken(
                        request.getUsername(),
                        request.getPassword()
                )
        );

        SecurityContextHolder.getContext().setAuthentication(authentication);

        String jwt = JwtUtils.generateToken(request.getUsername());

        return ResponseEntity.ok(new JwtAuthenticationResponse(jwt));
    }
}

AuthenticationController中,我们通过AuthenticationManager对用户进行身份验证。验证成功后,我们将生成的JWT返回给客户端。

结尾

至此,我们已经成功地使用Spring Boot整合了Spring Security和JWT。通过登录API生成的JWT,我们可以在后续的请求中进行身份验证,并授权特定用户访问不同的API。这样,我们就可以为我们的Web应用提供更安全的保障。

希望本文对您有所帮助,感谢阅读!

参考资料:


全部评论: 0

    我有话说: