SpringBoot集成SpringSecurity JWT实现权限管理

风华绝代 2024-06-15 ⋅ 18 阅读

在开发Web应用程序的过程中,安全性是不可忽视的一个重要方面。Spring Security是一个功能强大的框架,为我们提供了用于身份验证、授权、团队权限管理等功能,而JWT (Json Web Token) 是一种轻量级的身份验证和授权机制,通过在客户端和服务器之间传递加密的 JSON 数据进行身份验证和授权。

本篇博客将介绍如何使用Spring Boot集成Spring Security和JWT,实现基于角色的权限管理。

准备工作

在开始之前,我们需要完成以下准备工作:

  1. 确保你已经安装了Java Development Kit (JDK) 和 Maven。
  2. 创建一个新的Spring Boot项目,可以使用Spring Initializr快速生成。

添加依赖

在开始集成Spring Security和JWT之前,我们首先需要添加相关的依赖。

pom.xml文件中添加以下依赖:

<dependencies>
    <!-- Spring Security -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    
    <!-- JWT -->
    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt</artifactId>
        <version>0.9.1</version>
    </dependency>
</dependencies>

完成添加依赖后,执行mvn install命令来更新并安装所需的依赖。

配置Spring Security

在集成Spring Security之前,我们需要进行一些基本的配置。

application.properties中添加以下配置:

# Spring Security 路径放行配置
security.ignored = /login

创建一个WebSecurityConfig类,继承自WebSecurityConfigurerAdapter,用于配置Spring Security:

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
                .authorizeRequests()
                .antMatchers("/login").permitAll()
                .anyRequest().authenticated()
                .and()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers(
                "/swagger-resources/**",
                "/v2/api-docs",
                "/swagger-ui.html",
                "/webjars/**"
        );
    }
}

在上述配置中,我们禁用了CSRF保护,允许/login路径的匿名访问,并且要求所有其他路径都需要进行身份验证。

实现用户登录和JWT生成

为了实现用户登录认证和生成JWT,我们需要创建一个UserController,其中包含logingenerateToken两个接口。

@RestController
public class UserController {

    @PostMapping("/login")
    public ResponseEntity<?> login(@RequestBody LoginRequest loginRequest) {
        // 身份验证逻辑
        // ...
        
        // 生成JWT
        String token = generateToken(userDetails);
        
        // 返回JWT
        return ResponseEntity.ok(new LoginResponse(token));
    }
    
    private String generateToken(UserDetails userDetails) {
        // 构建JWT并返回token
        // ...
    }
}

generateToken方法中,我们使用JJWT库来生成JWT。

实现JWT验证和权限管理

创建一个JwtTokenFilter类,用于验证JWT并实现基于角色的权限管理。

public class JwtTokenFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        try {
            String jwt = extractJwtFromRequest(request);
            
            if (StringUtils.hasText(jwt) && validateToken(jwt)) {
                UserDetails userDetails = getUserDetailsFromToken(jwt);
                UsernamePasswordAuthenticationToken authentication = 
                        new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
                authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                SecurityContextHolder.getContext().setAuthentication(authentication);
            }
        } catch (Exception ex) {
            logger.error("Could not set user authentication in security context", ex);
        }
        
        filterChain.doFilter(request, response);
    }
    
    // ...
}

在上述代码中,doFilterInternal方法会从请求中提取JWT,并根据JWT验证用户身份。如果验证通过,将用户详细信息和权限添加到SecurityContextHolder中。

接下来,我们需要创建一个JwtTokenProvider类,用于处理JWT的生成和验证逻辑。

@Service
public class JwtTokenProvider {

    private static final String SECRET_KEY = "your-secret-key";
    private static final long EXPIRATION_TIME = 60 * 60 * 1000; // 1 hour

    public String generateToken(UserDetails userDetails) {
        Map<String, Object> claims = new HashMap<>();
        claims.put("roles", userDetails.getAuthorities().stream()
                .map(GrantedAuthority::getAuthority)
                .collect(Collectors.toList()));

        Date now = new Date();
        Date expirationDate = new Date(now.getTime() + EXPIRATION_TIME);

        return Jwts.builder()
                .setClaims(claims)
                .setSubject(userDetails.getUsername())
                .setIssuedAt(now)
                .setExpiration(expirationDate)
                .signWith(SignatureAlgorithm.HS256, SECRET_KEY)
                .compact();
    }

    public boolean validateToken(String token) {
        try {
            Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token);
            return true;
        } catch (Exception ex) {
            logger.error("Invalid JWT token", ex);
        }

        return false;
    }
    
    public UserDetails getUserDetailsFromToken(String token) {
        Claims claims = Jwts.parser()
                .setSigningKey(SECRET_KEY)
                .parseClaimsJws(token)
                .getBody();

        String username = claims.getSubject();

        List<GrantedAuthority> authorities = ((List<?>) claims.get("roles"))
                .stream()
                .map(authority -> new SimpleGrantedAuthority((String) authority))
                .collect(Collectors.toList());

        return new User(username, "", authorities);
    }
}

JwtTokenProvider中,我们使用了一个密钥和过期时间来生成JWT,并提供了验证和从JWT提取用户详细信息的方法。

整合Spring Security和JWT

为了实现Spring Security和JWT的整合,我们需要在WebSecurityConfig类中添加以下配置:

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    
    // ...

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // ...

        // 添加JWT过滤器
        http.addFilterBefore(jwtTokenFilter(), UsernamePasswordAuthenticationFilter.class);
    }
    
    @Bean
    public JwtTokenFilter jwtTokenFilter() {
        return new JwtTokenFilter();
    }
    
    @Bean
    public JwtTokenProvider jwtTokenProvider() {
        return new JwtTokenProvider();
    }
}

这样,当请求经过JwtTokenFilter时,将会自动验证JWT并设置用户的认证信息。

至此,我们完成了Spring Boot集成Spring Security和JWT的配置。可以通过/login接口进行用户登录认证并获取JWT。在其他需要权限认证的接口上,只需要在对应Controller上添加@PreAuthorize注解,即可实现基于角色的权限管理。

本篇博客介绍了如何使用Spring Boot集成Spring Security和JWT,实现了基于角色的权限管理。通过仔细阅读并实践相关代码,相信您已经掌握了这门技术。希望本篇博客对您有所帮助,谢谢阅读!

参考链接:


全部评论: 0

    我有话说: