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

devtools/2024/9/24 21:22:10/

前言

在企业级开发或者我们自己的课程设计中,确保用户数据的安全性和访问控制非常重要。而Spring Security和JWT是都两个强大的工具,它俩结合可以帮助我们实现这一目标。

Spring Security提供了全面的安全功能,而JWT则是一种用于身份验证的令牌机制。
在这里插入图片描述

JWT简单介绍

前面两个章节介绍过了Spring Security,这里就不再赘述了!!!

JWT是一种轻量级的身份验证和授权机制,通过发送包含用户信息的加密令牌来实现身份验证。这个工具我们在前面的文章中也提起过。

整合步骤与代码实现

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

在这里插入图片描述

添加依赖
首先,我们需要在项目的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>

接下来配置Spring Security,在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);  }   
}

实现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());

在这里插入图片描述

创建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());  }  
}

创建控制层LoginController

RestController  
@RequestMapping("/security")  
public class AuthenticationController {  @Autowired  private CustomAuthenticationManager authenticationManager;  @Autowired  private JwtTokenUtil jwtTokenUtil;  @Autowired  private UserDetailsService userDetailsService;  @PostMapping("/login")public ResponseEntity<?> createAuthenticationToken(@Valid @RequestBody LoginRequest loginRequest) throws Exception {  authenticate(loginRequest.getUsername(), loginRequest.getPassword());  final UserDetails userDetails = userDetailsService.loadUserByUsername(loginRequest.getUsername());  final String token = jwtTokenUtil.generateToken(userDetails);  return ResponseEntity.ok(new JwtAuthenticationResponse(token));  }  private void authenticate(String username, String password) throws Exception {  try {  authenticationManager.authenticate(  new UsernamePasswordAuthenticationToken(username, password)  );  } catch (DisabledException e) {  throw new Exception("USER_DISABLED", e);  } catch (BadCredentialsException e) {  throw new Exception("INVALID_CREDENTIALS", e);  }  }  
}

使用ApiFox测试

在这里插入图片描述
这样,我们就可以构建一个安全且高效的Web应用了。

小结

Spring Security提供了强大的身份验证和授权功能,而JWT则提供了一种轻量级的令牌机制来验证用户身份。通过结合使用,我们可以实现无缝的用户身份验证和访问控制,然后保护我们应用的数据安全

文章到这里就先结束了,后续会继续分享相关的知识点。
在这里插入图片描述


http://www.ppmy.cn/devtools/22208.html

相关文章

【JavaScriptThreejs】判断路径在二维平面上投影的方向顺逆时针

原理分析 可以将路径每个连续的两点向量叉乘相加&#xff0c;根据正负性判断路径顺逆时针性 当我们计算两个向量的叉积时&#xff0c;结果是一个新的向量&#xff0c;其方向垂直于这两个向量所在的平面&#xff0c;并且其大小与这两个向量构成的平行四边形的面积成正比。这个新…

python笔记:gensim进行LDA

理论部分&#xff1a;NLP 笔记&#xff1a;Latent Dirichlet Allocation &#xff08;介绍篇&#xff09;-CSDN博客 参考内容&#xff1a;DengYangyong/LDA_gensim: 用gensim训练LDA模型&#xff0c;进行新闻文本主题分析 (github.com) 1 导入库 import jieba,os,re from ge…

什么?你还不懂文件系统和软硬链接?

文章目录 序言认识磁盘磁盘在系统中的管理熟悉磁盘各个分区 软硬链接软链接硬链接 序言 首先熟悉一下一些专有名词(了解即可,但必须有一个概念认识) 固态:SSD,笔记本中常装的,台式机中也可以装,常见的对应接口M.2和SATA接口 磁盘:90年代常用的数据存储设备,或是现在企业级数据…

PotatoPie 4.0 实验教程(23) —— FPGA实现摄像头图像伽马(Gamma)变换

为什么要进行Gamma校正 图像的 gamma 校正是一种图像处理技术&#xff0c;用于调整图像的亮度和对比度&#xff0c;让显示设备显示的亮度和对比度更符合人眼的感知。Gamma 校正主要用于修正显示设备的非线性响应&#xff0c;以及在图像处理中进行色彩校正和图像增强。 以前&am…

Paddle OCR v4 微调训练文字识别SVTRNet模型实践

文字识别步骤参考&#xff1a;https://github.com/PaddlePaddle/PaddleOCR/blob/main/doc/doc_ch/recognition.md 微调步骤参考:https://github.com/PaddlePaddle/PaddleOCR/blob/release/2.7.1/doc/doc_ch/finetune.md 训练必要性 原始模型标点符号和括号容易识别不到 数据…

IPD项目管理体系的建立以及项目管理软件如何助力IPD高效落地

市场竞争的加剧与客户需求的多样性&#xff0c;传统的研发管理模式已无法满足企业发展的进程&#xff0c;IPD作为一种先进的研发管理思想与方法已被更多的企业所采用&#xff0c;用以提高研发效率。建立一个高效的IPD流程管理体系对于企业的发展至关重要。 接下来&#xff0c;…

Shader实战(3):贴图像素化风格实现

话不多说&#xff0c;将以下shader赋给材质贴上贴图即可。 Shader "HQY/Shader2" //自己改名 {Properties{_Diffuse ("Diffuse", Color) (1,1,1,1)_MainTex ("MainTex", 2D) "white" {}_Specular("Specular", Color) (…

mac M2 配置item2 rzsz

背景 apple m 系列处理器安装的 homebrew 跟 intel 处理器略有不同&#xff0c;其中安装目录的区别&#xff1a; m 系列处理器安装目录为 /usr/local/bin/homebrew intel 处理器安装目录为 /opt/homebrew 问题1: 卡住 产生原因&#xff1a; m 系列使用 brew install lrzs…