token验证的机理和简单实现

摘要:token验证,机制实现

一、cookie验证和token验证的比较

    cookie验证的机制是通过在客户端生成cookie,在服务器端生成session,然后每次请求时通过核对前端传来的cookie和服务器端session是否一致来管理用户的状态。当我们关闭浏览器的时候session会被释放,而cookie也可以自定义失效时间使其在一定时间内失效。

    token的验证机制放弃了session,为用户生成包含各类信息的一个集合,将其编码为一个令牌(token),将这个令牌发给前端,前端每次需要验证身份时携带令牌,服务器检查令牌的合法性以验证身份。简化了服务器的存储,但是要比session+cookie的验证方式更加消耗计算。是一种以时间换空间的方式。


相较于cookie验证的几点好处:

  1. 支持跨域:cookie是不支持跨域的,token则可以做到。这一点对于我们项目深有感触,前端和后台是分离开发和测试,前端的测试常常需要跨域测试,没有token之前总是统一汇总测试,很麻烦,也想当拖节奏。
  2. 无状态:因为cookie验证是依赖于session的状态的,而token则完全不用在session中保存任何数据,所以可以做到无状态,只要有token并且合法就可以。

   二、token的组成和简单实现

token主要有三个部分组成:头部,载荷,签名。

头部和载荷使用的是base64编码的,签名则是使用的HS256。当时不是很明白这个HS256,实际上就是HMAC using SHA-256。一种带密钥的加密。base64可以利用java有自带的工具Base64Encoder。

具体参考下面的博客:

https://blog.csdn.net/weixin_39800144/article/details/78853323

我们通过一个类来实现这个token的功能

此处使用了导入了Jackson的jar包,方便将对象转化为json,不然需要手动的转化。

import java.util.Date;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.thoughtworks.xstream.core.util.Base64Encoder;
import com.zhiku.util.Data;

public class Token {
	
	//JWT的header,实际工作中从配置文件中获得
	public static String Header = "{\"typ\": \"JWT\",\"alg\": \"HS256\"}";
	//密钥
	public static String SECRET_KEY = "secret";
	//base64编码工具
	public static Base64Encoder be = new Base64Encoder();
	
	public static String getToken(Data message) throws Exception{
		String header = setHeader();
		String payload = setPayload(message);
		String signature = setSignature(header + "." + payload);
		return header + "." + payload + "." + signature;
		
	}

	/**
	 * 设置JWT的荷载payload,荷载中包含主要的信息
	 * @param payload 一个可以格式化为json的对象
	 * @return
	 * @throws Exception
	 */
	public static String setPayload(Object payload) throws Exception{
		String base64_payload = om.writeValueAsString(payload);
		return be.encode(base64_payload.getBytes());
	}
	
	public static Object getPayload(String base64_payload) throws Exception{
		byte[] h = be.decode(base64_payload);
		return om.readValue(h, Object.class);
	}
	
	/**
	 * 设置header
	 * 获取已有的header,然后生成base64编码
	 * @return
	 */
	public static String setHeader(){
		return be.encode(Header.getBytes());
	}
	
	/**
	 * 解析header
	 * @param base64_Header	base64编码的header部分
	 * @return	返回解码后的header,以一个data的形式返回对应的json
	 * @throws Exception	在将json转变为data对象时可能出现异常
	 */
	public static Object getHeader(String base64_Header) throws Exception{
		byte[] h = be.decode(base64_Header);
		return om.readValue(h, Object.class);
	}
	
	/**
	 * 将header和payload使用HS256加密
	 * 然后对加密信息进行base64编码
	 * 生成JWT的签名
	 * @param message	header.payload
	 * @return	JWT的签名
	 * @throws Exception
	 */
	public static String setSignature(String message) throws Exception{
		Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
	    SecretKeySpec secret_key = new SecretKeySpec(SECRET_KEY.getBytes(), "HmacSHA256");
	    sha256_HMAC.init(secret_key);

	    String hash = be.encode(sha256_HMAC.doFinal(message.getBytes()));
	    return hash;
	}
	
	/**
	 * 验证token的正确性
	 * @param token	待验证token
	 * @return	token是否正确
	 */
	public static boolean testSign(String token){
		boolean equal = false;
		String fore_message = token.substring(0,token.lastIndexOf('.'));
		String sign = token.substring(token.lastIndexOf('.')+1);
		try{
			if(sign.equals(setSignature(fore_message))){
				equal = true;
			}else{
				equal = false;
			}
		}catch(Exception e){
			equal = false;
		}
		return equal;
	}
	
	
}

注意:在经过base64转码之后有些时候会包含“+”,在前端传给后台的时候,“+”会变成“ ”(加号变成了空格)。这个时候就会影响token的验证,可以使用字符串将其替换。


  • 3
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Boot可以使用JWT(JSON Web Token实现Token验证。JWT是一种基于JSON的开放标准,用于在各方之间安全地传输信息。它由三部分组成:头部、载荷和签名。 在Spring Boot中,可以使用Spring Security和jjwt库来实现JWT Token验证。首先需要添加依赖: ``` <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> <version>0.9.1</version> </dependency> ``` 然后在Spring Security的配置类中添加JWT Token验证的配置: ``` @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint; @Autowired private JwtRequestFilter jwtRequestFilter; @Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { // configure AuthenticationManagerBuilder } @Bean public PasswordEncoder passwordEncoder() { return NoOpPasswordEncoder.getInstance(); } @Bean public AuthenticationManager authenticationManagerBean() throws Exception { return super.authenticationManagerBean(); } @Override protected void configure(HttpSecurity httpSecurity) throws Exception { httpSecurity.csrf().disable() .authorizeRequests().antMatchers("/authenticate").permitAll(). anyRequest().authenticated().and(). exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint).and().sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.STATELESS); httpSecurity.addFilterBefore(jwtRequestFilter, UsernamePasswordAuthenticationFilter.class); } } ``` 其中,JwtAuthenticationEntryPoint是一个自定义的认证入口,用于处理未经授权的请求;JwtRequestFilter是一个自定义的过滤器,用于从请求中提取JWT Token并进行验证。 最后,需要实现一个JwtTokenUtil类,用于生成和验证JWT Token: ``` @Component public class JwtTokenUtil { private String secret = "mySecret"; public String generateToken(UserDetails userDetails) { Map<String, Object> claims = new HashMap<>(); return doGenerateToken(claims, userDetails.getUsername()); } private String doGenerateToken(Map<String, Object> claims, String subject) { long expirationTime = 1000 * 60 * 60 * 10; // 10 hours Date now = new Date(); Date expirationDate = new Date(now.getTime() + expirationTime); return Jwts.builder().setClaims(claims).setSubject(subject).setIssuedAt(now) .setExpiration(expirationDate).signWith(SignatureAlgorithm.HS512, secret).compact(); } 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()); } private Date getExpirationDateFromToken(String token) { return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody().getExpiration(); } private String getUsernameFromToken(String token) { return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody().getSubject(); } } ``` 这个类中,generateToken方法用于生成JWT Token,validateToken方法用于验证JWT Token是否有效。在生成JWT Token时,需要设置过期时间和签名算法。在验证JWT Token时,需要验证签名和过期时间。 这样就可以使用Spring Boot实现JWT Token验证了。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值