【Spring Security系列】Spring Security整合JWT:构建安全的Web应用 ...

打印 上一主题 下一主题

主题 905|帖子 905|积分 2715

前言

在企业级开辟或者我们本身的课程设计中,确保用户数据的安全性和访问控制非常紧张。而Spring Security和JWT是都两个强大的工具,它俩结合可以资助我们实现这一目标。
Spring Security提供了全面的安全功能,而JWT则是一种用于身份验证的令牌机制。

JWT简朴介绍

前面两个章节介绍过了Spring Security,这里就不再赘述了!!!
JWT是一种轻量级的身份验证和授权机制,通过发送包罗用户信息的加密令牌来实现身份验证。这个工具我们在前面的文章中也提起过。
整合步调与代码实现

目前大部门项目,大多数是使用前后端分离的模式。前后端分离的环境下,我们使用SpringSecurity解决权限题目的最常见的方案就是SpringSecurity+JWT 。

添加依赖
首先,我们需要在项目标pom.xml文件中添加Spring Security和JWT的依赖:
  1. <!--JWT-->
  2. <dependency>
  3.     <groupId>com.auth0</groupId>
  4.     <artifactId>java-jwt</artifactId>
  5.     <version>3.8.1</version>
  6. </dependency>
  7. <dependency>
  8.     <groupId>io.jsonwebtoken</groupId>
  9.     <artifactId>jjwt</artifactId>
  10.     <version>0.9.1</version>
  11. </dependency>
  12. <!--工具包-->
  13. <dependency>
  14.     <groupId>cn.hutool</groupId>
  15.     <artifactId>hutool-all</artifactId>
  16.     <version>5.8.0.M3</version>
  17. </dependency>
  18. <dependency>
  19.     <groupId>javax.xml.bind</groupId>
  20.     <artifactId>jaxb-api</artifactId>
  21. </dependency>
  22. <dependency>
  23.     <groupId>com.alibaba</groupId>
  24.     <artifactId>fastjson</artifactId>
  25.     <version>1.2.75</version>
  26. </dependency>
复制代码
接下来配置Spring Security,在Spring Security配置类中,我们自定义用户详情服务和认证管理器,并配置HTTP安全策略:
  1. @Configuration  
  2. @EnableWebSecurity  
  3. public class SecurityConfig extends WebSecurityConfigurerAdapter {  
  4.   
  5.     @Autowired  
  6.     private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;  
  7.   
  8.     @Autowired  
  9.     private JwtRequestFilter jwtRequestFilter;  
  10.   
  11.     @Autowired  
  12.     public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {  
  13.         auth.userDetailsService(userDetailsService()).passwordEncoder(passwordEncoder());  
  14.     }  
  15.   
  16.     @Bean  
  17.     @Override  
  18.     public AuthenticationManager authenticationManagerBean() throws Exception {  
  19.         return super.authenticationManagerBean();  
  20.     }  
  21.   
  22.     @Bean  
  23.     public PasswordEncoder passwordEncoder() {  
  24.         return new BCryptPasswordEncoder();  
  25.     }  
  26.   
  27.     @Override  
  28.     protected void configure(HttpSecurity http) throws Exception {  
  29.         http  
  30.             .csrf().disable()  
  31.             .authorizeRequests()  
  32.             .antMatchers("/authenticate").permitAll()  
  33.             .anyRequest().authenticated()  
  34.             .and()  
  35.             .exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint)  
  36.             .and()  
  37.             .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);  
  38.   
  39.         http.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class);  
  40.     }   
  41. }
复制代码
实现JWT天生和验证,我们创建一个JWT工具类,用于天生和剖析JWT:
  1. java
  2. @Component  
  3. public class JwtTokenUtil {  
  4.   
  5.     private String secret = "your_secret_key"; // 私钥,用于签名JWT  
  6.   
  7.     public String generateToken(UserDetails userDetails) {  
  8.         Map<String, Object> claims = new HashMap<>();  
  9.         return Jwts.builder()  
  10.                 .setClaims(claims)  
  11.                 .setSubject(((User) userDetails).getUsername())  
  12.                 .setIssuedAt(new Date(System.currentTimeMillis()))  
  13.                 .setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 10)) // 10小时过期  
  14.                 .signWith(SignatureAlgorithm.HS512, secret)  
  15.                 .compact();  
  16.     }  
  17.   
  18.     public String getUsernameFromToken(String token) {  
  19.         return getClaimFromToken(token, Claims::getSubject);  
  20.     }  
  21.   
  22.     public Date getExpirationDateFromToken(String token) {  
  23.         return getClaimFromToken(token, Claims::getExpiration);  
  24.     }  
  25.   
  26.     private <T> T getClaimFromToken(String token, Function<Claims, T> claimsResolver) {  
  27.         final Claims claims = getAllClaimsFromToken(token);  
  28.         return claimsResolver.apply(claims);  
  29.     }  
  30.   
  31.     private Claims getAllClaimsFromToken(String token) {  
  32.         return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();  
  33.     }  
  34.   
  35.     public boolean validateToken(String token, UserDetails userDetails) {  
  36.         final String username = getUsernameFromToken(token);  
  37.         return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));  
  38.     }  
  39.   
  40.     private boolean isTokenExpired(String token) {  
  41.         final Date expiration = getExpirationDateFromToken(token);  
  42.         return expiration.before(new Date());
复制代码

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

为了在用户每次请求时验证JWT,我们需要创建一个自定义的过滤器。同时,我们还需要一个认证管理器来处理用户的登录请求。
我们实现JWT过滤器
  1. @Component  
  2. public class JwtRequestFilter extends OncePerRequestFilter {  
  3.   
  4.     @Autowired  
  5.     private JwtTokenUtil jwtTokenUtil;  
  6.   
  7.     @Autowired  
  8.     private UserDetailsService userDetailsService;  
  9.   
  10.     @Override  
  11.     protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)  
  12.             throws ServletException, IOException {  
  13.         final String requestTokenHeader = request.getHeader("Authorization");  
  14.   
  15.         String username = null;  
  16.         String jwtToken = null;  
  17.         if (requestTokenHeader != null && requestTokenHeader.startsWith("Bearer ")) {  
  18.             jwtToken = requestTokenHeader.substring(7);  
  19.             try {  
  20.                 username = jwtTokenUtil.getUsernameFromToken(jwtToken);  
  21.             } catch (Exception e) {  
  22.                 logger.error("Unable to get JWT Token");  
  23.             }  
  24.         }  
  25.   
  26.         if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {  
  27.             UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);  
  28.   
  29.             if (jwtTokenUtil.validateToken(jwtToken, userDetails)) {  
  30.                 UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken =  
  31.                         new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());  
  32.                 usernamePasswordAuthenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));  
  33.                 SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);  
  34.             }  
  35.         }  
  36.   
  37.         filterChain.doFilter(request, response);  
  38.     }  
  39. }
复制代码
认证管理器,我们创建一个AuthenticationManager的实现来处理用户的登录请求:
  1. @Service  
  2. public class CustomAuthenticationManager implements AuthenticationManager {  
  3.   
  4.     @Autowired  
  5.     private UserDetailsService userDetailsService;  
  6.   
  7.     @Autowired  
  8.     private PasswordEncoder passwordEncoder;  
  9.   
  10.     @Override  
  11.     public Authentication authenticate(Authentication authentication) throws AuthenticationException {  
  12.         String username = authentication.getName();  
  13.         String password = authentication.getCredentials().toString();  
  14.   
  15.         UserDetails userDetails = userDetailsService.loadUserByUsername(username);  
  16.   
  17.         if (userDetails == null) {  
  18.             throw new BadCredentialsException("User not found");  
  19.         }  
  20.   
  21.         if (!passwordEncoder.matches(password, userDetails.getPassword())) {  
  22.             throw new BadCredentialsException("Wrong password");  
  23.         }  
  24.   
  25.         return new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());  
  26.     }  
  27. }
复制代码
创建控制层LoginController
  1. RestController  
  2. @RequestMapping("/security")  
  3. public class AuthenticationController {  
  4.   
  5.     @Autowired  
  6.     private CustomAuthenticationManager authenticationManager;  
  7.   
  8.     @Autowired  
  9.     private JwtTokenUtil jwtTokenUtil;  
  10.   
  11.     @Autowired  
  12.     private UserDetailsService userDetailsService;  
  13.   
  14.     @PostMapping("/login")
  15.     public ResponseEntity<?> createAuthenticationToken(@Valid @RequestBody LoginRequest loginRequest) throws Exception {  
  16.         authenticate(loginRequest.getUsername(), loginRequest.getPassword());  
  17.         final UserDetails userDetails = userDetailsService.loadUserByUsername(loginRequest.getUsername());  
  18.         final String token = jwtTokenUtil.generateToken(userDetails);  
  19.         return ResponseEntity.ok(new JwtAuthenticationResponse(token));  
  20.     }  
  21.   
  22.     private void authenticate(String username, String password) throws Exception {  
  23.         try {  
  24.             authenticationManager.authenticate(  
  25.                     new UsernamePasswordAuthenticationToken(username, password)  
  26.             );  
  27.         } catch (DisabledException e) {  
  28.             throw new Exception("USER_DISABLED", e);  
  29.         } catch (BadCredentialsException e) {  
  30.             throw new Exception("INVALID_CREDENTIALS", e);  
  31.         }  
  32.     }  
  33. }
复制代码
使用ApiFox测试


这样,我们就可以构建一个安全且高效的Web应用了。
小结

Spring Security提供了强大的身份验证和授权功能,而JWT则提供了一种轻量级的令牌机制来验证用户身份。通过结合使用,我们可以实现无缝的用户身份验证和访问控制,然后掩护我们应用的数据安全。
文章到这里就先结束了,后续会继续分享相关的知识点。


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

商道如狼道

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表