SpringBoot实战:Spring Security整合JWT:构建安全的Web应用

引言

在企业级应用开发或是我们精心设计的课程项目中,保障用户数据的安全与实现精细的访问控制策略是至关重要的。Spring Security凭借其全面的安全特性成为了一个不可或缺的框架,而JSON Web Tokens (JWT) 则作为一种高效的身份验证令牌机制,两者结合使用能够极大地增强应用的安全性。Spring Security负责构建全面的安全防线,而JWT则以其无状态、易于传输的特点,在身份验证与授权过程中发挥关键作用。


一、JWT简单介绍

JWT(JSON Web Tokens)是一种轻量级的、高效的身份验证和授权机制,它通过在网络请求中发送包含用户身份及权限信息的加密令牌来实现身份验证与授权。这种令牌机制因其无状态、易于跨域传输的特性而备受青睐。


二、代码示例

目前大部分项目,大多数都是使用前后端分离的模式。在前后端分离的情况下, 使用 SpringSecurity 解决权限问最常见的方案就是使用 SpringSecurity+JWT 方式。

SpringBoot实战:Spring Security整合JWT:构建安全的Web应用

  1. 在pom.xml文件中添加依赖:

首先我们需要在项目的pom.xml文件中添加 关于 Spring Security 和 JWT 的依赖

<!--JWT-->
<dependency>
    <groupId>com.auth0</groupId>
    <artifactId>java-jwt</artifactId>
    <version>3.8.1</version>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.1</version>
</dependency>
<!--工具包-->
<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.8.0.M3</version>
</dependency>
<dependency>
    <groupId>javax.xml.bind</groupId>
    <artifactId>jaxb-api</artifactId>
</dependency>
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.75</version>
</dependency>

2. 配置 Spring Security 

我们在自定义用户详情服务和认证管理器,并配置 HTTP 安全策略。

@Configuration  
@EnableWebSecurity  
public class SecurityConfig extends WebSecurityConfigurerAdapter {
  
    @Autowired  
    private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
  
    @Autowired  
    private JwtRequestFilter jwtRequestFilter;
  
    @Autowired  
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService()).passwordEncoder(passwordEncoder());
    }
  
    @Bean  
    @Override  
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }
  
    @Bean  
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
  
    @Override  
    protected void configure(HttpSecurity http) throws Exception {
        http  
            .csrf().disable()
            .authorizeRequests()
            .antMatchers("/authenticate").permitAll()
            .anyRequest().authenticated()
            .and()
            .exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint)
            .and()
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
  
        http.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);
    }
}

3.实现 JWT 生成和验证码:

接下来创建一个 JWT 工具类,用来生成和解析 JWT

java
@Component  
public class JwtTokenUtil {
  
    private String secret = "your_secret_key"; // 私钥,用于签名JWT
  
    public String generateToken(UserDetails userDetails) {
        Map<String, Object> claims = new HashMap<>();
        return Jwts.builder()
                .setClaims(claims)
                .setSubject(((User) userDetails).getUsername())
                .setIssuedAt(new Date(System.currentTimeMillis()))
                .setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 10)) // 10小时过期
                .signWith(SignatureAlgorithm.HS512, secret)
                .compact();
    }
  
    public String getUsernameFromToken(String token) {
        return getClaimFromToken(token, Claims::getSubject);
    }
  
    public Date getExpirationDateFromToken(String token) {
        return getClaimFromToken(token, Claims::getExpiration);
    }
  
    private <T> T getClaimFromToken(String token, Function<Claims, T> claimsResolver) {
        final Claims claims = getAllClaimsFromToken(token);
        return claimsResolver.apply(claims);
    }
  
    private Claims getAllClaimsFromToken(String token) {
        return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
    }
  
    public boolean validateToken(String token, UserDetails userDetails) {
        final String username = getUsernameFromToken(token);
        return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
    }
  
    private boolean isTokenExpired(String token) {
        final Date expiration = getExpirationDateFromToken(token);
        return expiration.before(new Date());

SpringBoot实战:Spring Security整合JWT:构建安全的Web应用

创建JWT过滤器与认证管理器

为了在用户每次请求时验证JWT,我们需要创建一个自定义的过滤器。同时,我们还需要一个认证管理器来处理用户的登录请求。

实现JWT过滤器

@Component  
public class JwtRequestFilter extends OncePerRequestFilter {
  
    @Autowired  
    private JwtTokenUtil jwtTokenUtil;
  
    @Autowired  
    private UserDetailsService userDetailsService;
  
    @Override  
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)  
            throws ServletException, IOException
{
        final String requestTokenHeader = request.getHeader("Authorization");
  
        String username = null;
        String jwtToken = null;
        if (requestTokenHeader != null && requestTokenHeader.startsWith("Bearer ")) {
            jwtToken = requestTokenHeader.substring(7);
            try {
                username = jwtTokenUtil.getUsernameFromToken(jwtToken);
            } catch (Exception e) {
                logger.error("Unable to get JWT Token");
            }
        }
  
        if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
            UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);
  
            if (jwtTokenUtil.validateToken(jwtToken, userDetails)) {
                UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken =
                        new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
                usernamePasswordAuthenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);
            }
        }
  
        filterChain.doFilter(request, response);
    }
}

创建一个AuthenticationManager的实现来处理用户的登录请求:

@Service  
public class CustomAuthenticationManager implements AuthenticationManager {
  
    @Autowired  
    private UserDetailsService userDetailsService;
  
    @Autowired  
    private PasswordEncoder passwordEncoder;
  
    @Override  
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        String username = authentication.getName();
        String password = authentication.getCredentials().toString();
  
        UserDetails userDetails = userDetailsService.loadUserByUsername(username);
  
        if (userDetails == null) {
            throw new BadCredentialsException("User not found");
        }
  
        if (!passwordEncoder.matches(password, userDetails.getPassword())) {
            throw new BadCredentialsException("Wrong password");
        }
  
        return new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
    }
}

代码测试:

SpringBoot实战:Spring Security整合JWT:构建安全的Web应用

引言

Spring Security提供了全面的身份验证和授权功能,确保了用户访问的安全性;而JWT(JSON Web Tokens)则提供了一种轻量级的、高效的令牌机制,用于验证用户的身份。通过将这两者结合使用,我们可以实现无缝且强健的用户身份验证流程与精细的访问控制策略,从而全方位地保护我们应用程序中的数据安全。



原文始发于微信公众号(Java技术前沿):SpringBoot实战:Spring Security整合JWT:构建安全的Web应用

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/299696.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!