Spring Boot Security JWT 授权验证模块使用

前端开发者说 2024-03-20 ⋅ 22 阅读

概述

在开发 Web 应用程序时,安全性是一项非常重要的问题。Spring Boot Security 提供了一种简单而强大的方式来保护应用程序免受各种安全威胁。结合使用 JWT(JSON Web Token),我们可以实现一种无状态的、分布式的授权验证机制。

本文将介绍如何使用 Spring Boot Security 和 JWT 来实现授权验证模块,以增强 Web 应用程序的安全性。

步骤

1. 添加 Maven 依赖

首先,在项目的 pom.xml 文件中添加以下 Maven 依赖:

<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>

这将添加 Spring Boot Security 和 JWT 相关的依赖项。

2. 创建用户认证服务

接下来,我们需要创建一个用户认证服务,在其中实现验证用户身份的逻辑。创建一个名为 UserService 的类,并实现 UserDetailsService 接口:

@Service
public class UserService implements UserDetailsService {

    @Autowired
    private UserRepository userRepository;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userRepository.findByUsername(username);
        if (user == null) {
            throw new UsernameNotFoundException("Invalid username or password.");
        }
        return new org.springframework.security.core.userdetails.User(user.getUsername(),
                user.getPassword(),
                Arrays.asList(new SimpleGrantedAuthority("ROLE_USER")));
    }
}

在上述代码中,我们使用 UserRepository 来查询数据库中的用户信息,并将其转换为实现了 UserDetails 接口的对象。这样,我们就可以在后续的认证过程中使用用户的信息。

3. 创建一个安全配置类

接下来,我们需要创建一个安全配置类,用于配置 Spring Boot Security 的行为。创建一个名为 SecurityConfig 的类,并加上 @EnableWebSecurity 注解,以启用 Spring Boot Security:

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserService userService;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
                .authorizeRequests()
                .antMatchers("/login").permitAll()
                .anyRequest().authenticated()
                .and()
                .addFilter(new JwtAuthenticationFilter(authenticationManager()))
                .addFilter(new JwtAuthorizationFilter(authenticationManager()));
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userService).passwordEncoder(passwordEncoder());
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

在上述代码中,我们通过重写 configure 方法来配置认证和授权规则。我们使用 permitAll() 允许所有用户访问 /login 路径,必须进行身份验证的其他请求将要求用户进行登录。我们还添加了两个 JWT 相关的过滤器:JwtAuthenticationFilterJwtAuthorizationFilter

4. 创建 JWT 过滤器

接下来,我们需要创建两个 JWT 过滤器:JwtAuthenticationFilterJwtAuthorizationFilterJwtAuthenticationFilter 用于验证用户身份,而 JwtAuthorizationFilter 用于验证用户的访问权限。

public class JwtAuthenticationFilter extends UsernamePasswordAuthenticationFilter {

    private final AuthenticationManager authenticationManager;

    public JwtAuthenticationFilter(AuthenticationManager authenticationManager) {
        this.authenticationManager = authenticationManager;
        setFilterProcessesUrl("/login");
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest req,
                                                HttpServletResponse res) throws AuthenticationException {
        try {
            LoginUser loginUser = new ObjectMapper().readValue(req.getInputStream(), LoginUser.class);
            return authenticationManager.authenticate(
                    new UsernamePasswordAuthenticationToken(loginUser.getUsername(), loginUser.getPassword())
            );
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    protected void successfulAuthentication(HttpServletRequest req,
                                            HttpServletResponse res,
                                            FilterChain chain,
                                            Authentication auth) throws IOException {
        String token = Jwts.builder()
                .setSubject(((User) auth.getPrincipal()).getUsername())
                .setExpiration(new Date(System.currentTimeMillis() + SecurityConstants.EXPIRATION_TIME))
                .signWith(SignatureAlgorithm.HS512, SecurityConstants.SECRET.getBytes())
                .compact();
        res.addHeader(SecurityConstants.HEADER_STRING, SecurityConstants.TOKEN_PREFIX + token);
    }
}
public class JwtAuthorizationFilter extends BasicAuthenticationFilter {

    public JwtAuthorizationFilter(AuthenticationManager authenticationManager) {
        super(authenticationManager);
    }

    @Override
    protected void doFilterInternal(HttpServletRequest req,
                                    HttpServletResponse res,
                                    FilterChain chain) throws IOException, ServletException {
        String header = req.getHeader(SecurityConstants.HEADER_STRING);
        if (header == null || !header.startsWith(SecurityConstants.TOKEN_PREFIX)) {
            chain.doFilter(req, res);
            return;
        }
        UsernamePasswordAuthenticationToken authentication = getAuthentication(req);
        SecurityContextHolder.getContext().setAuthentication(authentication);
        chain.doFilter(req, res);
    }

    private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request) {
        String token = request.getHeader(SecurityConstants.HEADER_STRING);
        if (token != null) {
            String username = Jwts.parser()
                    .setSigningKey(SecurityConstants.SECRET.getBytes())
                    .parseClaimsJws(token.replace(SecurityConstants.TOKEN_PREFIX, ""))
                    .getBody()
                    .getSubject();
            if (username != null) {
                return new UsernamePasswordAuthenticationToken(username, null, new ArrayList<>());
            }
            return null;
        }
        return null;
    }
}

在上述代码中,我们使用 JWT 来生成用户认证和授权所需的令牌。

5. 创建登录接口

最后,我们需要创建一个登录接口,以供用户进行身份验证。创建一个名为 AuthController 的类,并添加登录接口:

@RestController
public class AuthController {

    @PostMapping("/login")
    public ResponseEntity<?> login(@RequestBody LoginUser loginUser) {
        // Validate user credentials and authenticate
        
        // Generate and return JWT token
    }
}

在上述代码中,我们接收一个包含用户名和密码的 LoginUser 对象,并进行身份验证。验证成功后,我们生成并返回一个 JWT 令牌。

结论

通过结合使用 Spring Boot Security 和 JWT,我们可以实现一个强大、安全的授权验证模块。在本文中,我们了解了如何使用 Spring Boot Security 配置认证规则,并创建了用户认证服务、安全配置类、JWT 过滤器和登录接口。这种组合可以帮助我们有效地保护 Web 应用程序免受未经授权的访问。

希望本文能对你理解 Spring Boot Security 和 JWT 的使用有所帮助,欢迎留言进行讨论和提问。


全部评论: 0

    我有话说: