网站空间和主机,wps wordpress,智能管理系统软件,网站优化外链#x1f38f;#xff1a;你只管努力#xff0c;剩下的交给时间 #x1f3e0; #xff1a;小破站 保护你的应用#xff1a;Spring Boot与JWT的黄金组合 前言第一#xff1a;项目整合基本配置token验证配置拦截器配置依赖的pom坐标 第二#xff1a;Spring Security与JWT的… 你只管努力剩下的交给时间 小破站 保护你的应用Spring Boot与JWT的黄金组合 前言第一项目整合基本配置token验证配置拦截器配置依赖的pom坐标 第二Spring Security与JWT的完美协作步骤1: 引入依赖步骤2: 配置Spring Security步骤3: 实现JWT工具类步骤4: 集成JWT到身份验证过滤器步骤5: 配置Spring Security使用JWT过滤器 第三实际应用场景1. 前后端分离应用2. 微服务架构3. 单点登录SSO4. Token刷新5. 动态权限调整6. 无状态服务7. 跨域资源共享CORS8. 移动应用身份验证 第四使用公钥/私钥对JWT进行签名1. 生成密钥对使用keytool生成密钥对使用开发库生成密钥对 2. 在Spring Boot中应用密钥对application.properties或application.yml中配置密钥配置类中加载密钥 3. 在JWT签名和验证中使用密钥对生成JWT并签名验证JWT签名 前言
在网络的世界中数据安全是应用开发的头等大事。而Spring Boot与JWT的完美结合就像是为你的应用添加了一把坚实的安全之锁。从今天开始让我们一同踏上这场奇妙的旅程揭开Spring Boot整合JWT的神秘面纱。
第一项目整合
基本配置
package test.bo.springbootjwt.util;import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;import java.util.Calendar;
import java.util.Map;public class JwtUtil {private static final String signXS4B#21!*;//生成token header payload signpublic static String getToken(MapString,String map){Calendar instance Calendar.getInstance();instance.add(Calendar.HOUR,24);//默认一天过期//创建jwt builderJWTCreator.Builder builder JWT.create();//payloadmap.forEach((k,v)-{builder.withClaim(k,v);});String token builder.withExpiresAt(instance.getTime())//指定令牌过期时间.sign(Algorithm.HMAC256(sign));return token;}//验证tokenpublic static DecodedJWT verify(String token){return JWT.require(Algorithm.HMAC256(sign)).build().verify(token);}//获取token信息方法public static DecodedJWT getTokerInfo(String token){DecodedJWT verify JWT.require(Algorithm.HMAC256(sign)).build().verify(token);return verify;}
}
token验证配置
package test.bo.springbootjwt.interceptors;import com.auth0.jwt.exceptions.AlgorithmMismatchException;
import com.auth0.jwt.exceptions.SignatureVerificationException;
import com.auth0.jwt.exceptions.TokenExpiredException;
import org.springframework.http.HttpMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import test.bo.springbootjwt.entity.Result;
import test.bo.springbootjwt.util.JwtUtil;import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class JWTInterceptor implements HandlerInterceptor {Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException, ServletException {if (HttpMethod.OPTIONS.toString().equals(request.getMethod())) {System.out.println(OPTIONS请求放行);return true;}response.setContentType(application/json;charsetUTF-8);Result result new Result();//获取请求头中令牌String token request.getHeader(token0001);System.out.println(token);try {JwtUtil.verify(token);//验证令牌return true;}catch (SignatureVerificationException e){e.printStackTrace();result.setMsg(无效签名);}catch (TokenExpiredException e){e.printStackTrace();result.setMsg(token过期);}catch (AlgorithmMismatchException e){e.printStackTrace();result.setMsg(token算法不一致);}catch (Exception e){e.printStackTrace();result.setMsg(token无效);}result.setState(false);/* String string new ObjectMapper().writeValueAsString(result);response.setContentType(application/json;charsetutf8);response.getWriter().println(string);*/return false;}}
拦截器配置
package test.bo.springbootjwt.config;import org.springframework.context.annotation.Configuration;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import test.bo.springbootjwt.interceptors.JWTInterceptor;Configuration
CrossOrigin
public class InterceptorConfig implements WebMvcConfigurer {public void addCorsMappings(CorsRegistry registry) {registry.addMapping(/**).allowedOrigins(*).allowedMethods(GET, HEAD, POST, PUT, DELETE, OPTIONS).maxAge(36000).allowedHeaders(*).allowCredentials(true);}Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(new JWTInterceptor()).addPathPatterns(/**) //其他接口token验证.excludePathPatterns(/user/login); //所有用户都放行}
}
依赖的pom坐标
dependencygroupIdcom.auth0/groupIdartifactIdjava-jwt/artifactIdversion3.10.3/version
/dependency第二Spring Security与JWT的完美协作
Spring Security和JWT的结合在现代Web应用中是一种常见的做法它提供了一种安全的身份验证和授权机制。以下是配置Spring Security以使用JWT进行身份验证的一般步骤
步骤1: 引入依赖
确保项目中引入了Spring Security和JWT的相关依赖。可以使用Maven或Gradle进行引入例如
Maven:
dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-security/artifactId
/dependency
dependencygroupIdio.jsonwebtoken/groupIdartifactIdjjwt/artifactIdversion0.9.1/version !-- 替换为最新版本 --
/dependencyGradle:
implementation org.springframework.boot:spring-boot-starter-security
implementation io.jsonwebtoken:jjwt:0.9.1 // 替换为最新版本步骤2: 配置Spring Security
在Spring Boot应用的配置类中配置Spring Security指定哪些路径需要进行身份验证哪些路径不需要。
Configuration
EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {Overrideprotected void configure(HttpSecurity http) throws Exception {http.csrf().disable().authorizeRequests().antMatchers(/public/**).permitAll() // 不需要身份验证的路径.anyRequest().authenticated().and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); // 禁用Session}
}步骤3: 实现JWT工具类
实现一个用于生成和解析JWT的工具类。可以使用JJWT库来简化这个过程。
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.security.core.userdetails.UserDetails;import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;public class JwtUtil {private static final String SECRET_KEY your_secret_key;public static String generateToken(UserDetails userDetails) {MapString, Object claims new HashMap();return createToken(claims, userDetails.getUsername());}private static String createToken(MapString, Object claims, String subject) {return Jwts.builder().setClaims(claims).setSubject(subject).setIssuedAt(new Date(System.currentTimeMillis())).setExpiration(new Date(System.currentTimeMillis() 1000 * 60 * 60 * 10)) // Token有效期10小时.signWith(SignatureAlgorithm.HS256, SECRET_KEY).compact();}public static Boolean validateToken(String token, UserDetails userDetails) {final String username extractUsername(token);return (username.equals(userDetails.getUsername()) !isTokenExpired(token));}private static Boolean isTokenExpired(String token) {return extractExpiration(token).before(new Date());}private static Date extractExpiration(String token) {return extractClaim(token, Claims::getExpiration);}private static T T extractClaim(String token, FunctionClaims, T claimsResolver) {final Claims claims extractAllClaims(token);return claimsResolver.apply(claims);}private static Claims extractAllClaims(String token) {return Jwts.parser().setSigningKey(SECRET_KEY).parseClaimsJws(token).getBody();}private static String extractUsername(String token) {return extractClaim(token, Claims::getSubject);}
}步骤4: 集成JWT到身份验证过滤器
创建一个自定义的过滤器用于在Spring Security的过滤链中验证JWT并将用户信息添加到Spring Security上下文。
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;public class JwtRequestFilter extends UsernamePasswordAuthenticationFilter {private final UserDetailsService userDetailsService;private final JwtUtil jwtUtil;public JwtRequestFilter(UserDetailsService userDetailsService, JwtUtil jwtUtil) {this.userDetailsService userDetailsService;this.jwtUtil jwtUtil;}Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)throws ServletException, IOException {final String authorizationHeader request.getHeader(Authorization);String username null;String jwt null;if (authorizationHeader ! null authorizationHeader.startsWith(Bearer )) {jwt authorizationHeader.substring(7);username jwtUtil.extractUsername(jwt);}if (username ! null SecurityContextHolder.getContext().getAuthentication() null) {UserDetails userDetails this.userDetailsService.loadUserByUsername(username);if (jwtUtil.validateToken(jwt, userDetails)) {UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());usernamePasswordAuthenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);}}chain.doFilter(request, response);}
}步骤5: 配置Spring Security使用JWT过滤器
在配置类中将自定义的JwtRequestFilter添加到Spring Security的过滤器链中。
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;Configuration
EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {Autowiredprivate UserDetailsService userDetailsService;Autowiredprivate JwtUtil jwtUtil;Overrideprotected void configure(HttpSecurity http) throws Exception {http.csrf().disable().authorizeRequests().antMatchers(/authenticate).permitAll() // 不需要身份验证的路径.anyRequest().authenticated().and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); // 禁用Sessionhttp.addFilterBefore(jwtRequestFilter(), UsernamePasswordAuthenticationFilter.class);}Beanpublic JwtRequestFilter jwtRequestFilter() {return new JwtRequestFilter(userDetailsService, jwtUtil);}Overridepublic void configure(AuthenticationManagerBuilder auth) throws Exception {auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());}Beanpublic PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();}OverrideBeanpublic AuthenticationManager authenticationManagerBean() throws Exception {return super.authenticationManagerBean();}
}以上是一个简单的集成Spring Security和JWT的例子实际项目中可能需要根据具体需求进行更复杂的配置和定制。这个例子涵盖了基本的认证、授权和JWT生成、解析的流程。
第三实际应用场景
Spring Boot整合JWT在实际应用中有许多灵活的应用场景特别适用于前后端分离和微服务架构。以下是一些实际应用场景的示例
1. 前后端分离应用
在前后端分离的应用中前端和后端是独立开发和部署的通过API进行通信。JWT可以在这种情境下提供简便的身份验证和授权机制。
用户认证 用户在前端登录后端验证用户名和密码如果通过后端生成JWT并返回给前端。后续请求 前端在每个请求的头部携带JWT后端通过JWT验证用户身份和权限。
2. 微服务架构
在微服务架构中每个服务都可以独立验证和授权用户而不需要在每个服务中保存用户的状态。JWT在这种场景下提供了轻量级的、无状态的身份验证方式。
用户认证 一个服务处理用户登录请求生成JWT返回给客户端。微服务间通信 客户端在与其他微服务通信时携带JWT进行身份验证每个微服务独立验证JWT。
3. 单点登录SSO
JWT可以用于实现单点登录使用户只需在系统中进行一次登录即可访问多个关联系统。
登录过程 用户在一个系统中登录获取JWT。访问其他系统 用户在其他系统中携带JWT其他系统通过验证JWT即可知晓用户身份。
4. Token刷新
JWT的过期机制使得可以轻松实现Token刷新提高了系统的安全性。
刷新流程 在JWT即将过期时客户端使用刷新令牌或重新提供用户凭证来获取新的JWT而无需重新输入用户名和密码。
5. 动态权限调整
在某些场景下用户的权限可能会在登录后发生变化。JWT中包含的信息可以灵活地调整用户的权限。
权限调整 当用户的权限发生变化时系统可以主动撤销旧的JWT生成包含新权限信息的JWT。
6. 无状态服务
JWT是无状态的服务端不需要存储用户的状态信息这对于水平扩展和微服务的部署非常有利。
水平扩展 可以轻松地将请求路由到不同的服务实例而不需要共享用户的状态。
7. 跨域资源共享CORS
在前后端分离的应用中处理跨域问题是常见的挑战。JWT可以在跨域环境中通过在响应头中包含Token来解决跨域问题。
CORS处理 后端在响应头中包含JWT前端在每次请求中都携带JWT以解决浏览器的同源策略限制。
8. 移动应用身份验证
JWT也可以用于移动应用的身份验证提供安全的身份验证机制同时减少对服务器的频繁请求。
移动应用 移动应用通过安全的方式将用户凭证传递给服务器获取JWT。JWT在请求中携带 移动应用在后续请求中携带JWT服务端验证并处理请求。
这些场景展示了Spring Boot整合JWT在不同应用场景下的灵活应用。通过使用JWT可以实现简单、安全且可扩展的身份验证和授权机制。
第四使用公钥/私钥对JWT进行签名
使用公钥/私钥对JWT进行签名可以提高系统的安全性确保只有拥有私钥的服务端才能生成有效的签名而其他服务端或客户端则可以通过公钥验证签名的合法性。以下是生成密钥对并在Spring Boot中应用的步骤
1. 生成密钥对
首先你需要生成公钥/私钥对。可以使用Java的keytool工具或者使用一些开发库来生成。
使用keytool生成密钥对
keytool -genkeypair -alias myjwtkey -keyalg RSA -keysize 2048 -storetype PKCS12 -keystore myjwtkeystore.p12 -validity 3650这将生成一个2048位的RSA密钥对并存储在名为myjwtkeystore.p12的文件中。
使用开发库生成密钥对
在Java中你也可以使用一些开发库例如Bouncy Castle生成密钥对。以下是一个使用Bouncy Castle库的示例
import org.bouncycastle.jce.provider.BouncyCastleProvider;import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.Security;public class KeyPairGeneratorExample {public static void main(String[] args) throws NoSuchProviderException, NoSuchAlgorithmException {Security.addProvider(new BouncyCastleProvider());KeyPairGenerator generator KeyPairGenerator.getInstance(RSA, BC);generator.initialize(2048); // key sizeKeyPair keyPair generator.generateKeyPair();// keyPair.getPrivate() 返回私钥// keyPair.getPublic() 返回公钥}
}2. 在Spring Boot中应用密钥对
在Spring Boot中可以通过配置文件将密钥加载到应用中以便在JWT的签名和验证过程中使用。
application.properties或application.yml中配置密钥
# 密钥的存储类型JKS或PKCS12等
jwt.key-store-typePKCS12
# 密钥库文件的路径
jwt.key-storeclasspath:myjwtkeystore.p12
# 密钥库的密码
jwt.key-store-passwordmypassword
# 别名
jwt.key-aliasmyjwtkey
# 密钥的密码
jwt.key-passwordmypassword配置类中加载密钥
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.Certificate;
import java.security.KeyStore;Configuration
public class JwtConfig {Value(${jwt.key-store-type})private String keyStoreType;Value(${jwt.key-store})private String keyStore;Value(${jwt.key-store-password})private String keyStorePassword;Value(${jwt.key-alias})private String keyAlias;Value(${jwt.key-password})private String keyPassword;Beanpublic KeyPair keyPair() throws Exception {KeyStore keystore KeyStore.getInstance(keyStoreType);keystore.load(getClass().getClassLoader().getResourceAsStream(keyStore), keyStorePassword.toCharArray());KeyPair keyPair new KeyPair(keystore.getCertificate(keyAlias).getPublicKey(),(PrivateKey) keystore.getKey(keyAlias, keyPassword.toCharArray()));return keyPair;}Beanpublic PasswordEncoder passwordEncoder() {return new BCryptPasswordEncoder();}
}这个配置类中的keyPair()方法通过加载密钥库获取公钥和私钥的方式创建了一个KeyPair对象以便在JWT的签名和验证中使用。
3. 在JWT签名和验证中使用密钥对
使用Spring Security和JWT库可以在签发和验证JWT时使用密钥对。
生成JWT并签名
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;import java.util.Date;Component
public class JwtUtil {Autowiredprivate KeyPair keyPair;public String generateToken(UserDetails userDetails) {return Jwts.builder().setSubject(userDetails.getUsername()).setIssuedAt(new Date()).setExpiration(new Date(System.currentTimeMillis() 1000 * 60 * 60 * 10)) // 10 hours.signWith(SignatureAlgorithm.RS256, keyPair.getPrivate()).compact();}
}验证JWT签名
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;import java.util.Date;Component
public class JwtUtil {Autowiredprivate KeyPair keyPair;public Boolean validateToken(String token, UserDetails userDetails) {Claims claims Jwts.parser().setSigningKey(keyPair.getPublic()).parseClaimsJws(token).getBody();return userDetails.getUsername().equals(claims.getSubject()) !isTokenExpired(token);}private Boolean isTokenExpired(String token) {Date expirationDate Jwts.parser().setSigningKey(keyPair.getPublic()).parseClaimsJws(token).getBody().getExpiration();return expirationDate.before(new Date());}
}在这个例子中SignatureAlgorithm.RS256指定了RSA算法使用keyPair.getPrivate()进行签名keyPair.getPublic()进行验证。
使用公钥/私钥对JWT进行签名和验证提供了更高的安全性确保只有拥有私钥的服务端才能生成有效的签名而其他服务端或客户端则可以通过公钥验证签名的合法性。