请叫我小C的gravatar头像
请叫我小C2018-08-22 15:32:27
SpringBoot RestFull API签名

一、需求如下

对指定的API路径进行签名认证,对于没有指定的无需认证,认证具体到方法。

二、查阅资料与开发

1.了解JWT,实际上用的开源jjwt

2.编写自定义注解

3.编写拦截器,主要是拦截特定的url进行签名验证,这里解析请求的handler是否有包含自定义注解

确定思路后,开始编写代码

A、写工具,我在网上找的,代码不复杂,一看就懂,代码如下

/**  
* @Title: TokenUtils.java 
* @Description:
* @Copyright: Copyright (c) 2018
* @Company:http://www.sinocon.cn
* @author Administrator  
* @date 2018年8月21日  
* @version 1.0  
*/
package cn.sinocon.hive.utils;

import java.security.Key;
import java.util.Date;

import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;

import cn.hutool.core.date.DateUtil;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

/**
 * @Title: TokenUtils
 * @Description:
 * @author:Administrator
 * @date 2018年8月21日
 */
public class TokenUtils {
	/**
	 * 签名秘钥
	 */
	public static final String SECRET = "LHqDYnwpy7jzhmWdIy7EW3ER64mNlAGKRZWLKFvSKIyWWX";

	/**
	 * 生成token
	 * 
	 * @param id
	 *            一般传入userName
	 * @return
	 */
	public static String createJwtToken(String id) {
		String issuer = "www.zuidaima.com";
		String subject = "8vfu3wqEidZve2";
		long ttlMillis = System.currentTimeMillis();
		return createJwtToken(id, issuer, subject, ttlMillis);
	}

	/**
	 * 生成Token
	 * 
	 * @param id
	 *            编号
	 * @param issuer
	 *            该JWT的签发者,是否使用是可选的
	 * @param subject
	 *            该JWT所面向的用户,是否使用是可选的;
	 * @param ttlMillis
	 *            签发时间
	 * @return token String
	 */
	public static String createJwtToken(String id, String issuer, String subject, long ttlMillis) {

		// 签名算法 ,将对token进行签名
		SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;

		// 生成签发时间
		long nowMillis = System.currentTimeMillis();
		Date now = new Date(nowMillis);

		// 通过秘钥签名JWT
		byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary(SECRET);
		Key signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName());

		// Let's set the JWT Claims
		JwtBuilder builder = Jwts.builder().setId(id).setIssuedAt(now).setSubject(subject).setIssuer(issuer)
				.signWith(signatureAlgorithm, signingKey);

		// if it has been specified, let's add the expiration
		if (ttlMillis >= 0) {
			long expMillis = nowMillis + ttlMillis;
			Date exp = new Date(expMillis);
			builder.setExpiration(exp);
		}

		// Builds the JWT and serializes it to a compact, URL-safe string
		return builder.compact();

	}

	// Sample method to validate and read the JWT
	public static Claims parseJWT(String jwt) {
		// This line will throw an exception if it is not a signed JWS (as
		// expected)
		Claims claims = Jwts.parser().setSigningKey(DatatypeConverter.parseBase64Binary(SECRET)).parseClaimsJws(jwt)
				.getBody();
		return claims;
	}

	public static void main(String[] args) {
		System.out.println(TokenUtils.createJwtToken("page=10"));
		

	}
}

 

B、编写注解

/**  
* @Title: RequireSignature.java 
* @Description:
* @Copyright: Copyright (c) 2018
* @Company:http://www.sinocon.cn
* @author Administrator  
* @date 2018年8月18日  
* @version 1.0  
*/  
package cn.sinocon.hive.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**  
* @Title: RequireSignature 
* @Description:
* @author:Administrator  
* @date 2018年8月18日  
*/
@Target({ElementType.METHOD})// 可用在方法名上
@Retention(RetentionPolicy.RUNTIME)// 运行时有效
public @interface  RequireSignature {
}

C。编写拦截器

/**  
* @Title: LoginInterceptor.java 
* @Description:
* @Copyright: Copyright (c) 2018
* @Company:http://www.sinocon.cn
* @author Administrator  
* @date 2018年8月18日  
* @version 1.0  
*/
package cn.sinocon.hive.interceptor;

import java.lang.reflect.Method;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang.StringUtils;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.sinocon.hive.annotation.RequireSignature;
import cn.sinocon.hive.utils.TokenUtils;
import io.jsonwebtoken.Claims;

/**
 * @Title: LoginInterceptor
 * @Description:
 * @author:Administrator
 * @date 2018年8月18日
 */
@Component
public class LoginInterceptor extends HandlerInterceptorAdapter {

	public final static String ACCESS_TOKEN = "accessToken";
	public final static String EXCEPTION_MSG = "signature does not match locally computed signature,error code:";

	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {

		if (!(handler instanceof HandlerMethod)) {
			return true;
		}

		HandlerMethod handlerMethod = (HandlerMethod) handler;
		Method method = handlerMethod.getMethod();

		RequireSignature methodAnnotation = method.getAnnotation(RequireSignature.class);

		// 有 @RequireSignature 注解,需要认证
		if (ObjectUtil.isNotNull(methodAnnotation)) {
			// 判断是否存在令牌信息,如果存在,则允许登录
			String accessToken = request.getParameter(ACCESS_TOKEN);
			if (StringUtils.isBlank(accessToken)) {
				// 需要认证才行
				throw new RuntimeException(EXCEPTION_MSG + "400003");
			}
			Claims claims = null;

			try {
				claims = TokenUtils.parseJWT(accessToken);
			} catch (Exception e) {
				throw new RuntimeException(EXCEPTION_MSG + "400005");
			}

			// 签名格式错误,请按照约定生成签名
			String[] firstParam = claims.getId().split("=");
			if (ObjectUtils.isEmpty(firstParam)) {
				throw new RuntimeException(EXCEPTION_MSG + "400005");
			}

			// 签名被篡改
			String parameter = request.getParameter(firstParam[0]);
			if (!firstParam[1].equals(parameter)) {
				throw new RuntimeException(EXCEPTION_MSG + "400006");
			}

			boolean validation = false;
			// 获取签名生成的时间,签名有效10分钟
			try {
				long timeInMillis = DateUtil.calendar(Long.parseLong(claims.get("exp") + "")).getTimeInMillis();
				validation = DateUtil.calendar(System.currentTimeMillis())
						.getTimeInMillis() < (timeInMillis + 10 * 60 * 1000);
			} catch (Exception e) {
				throw new RuntimeException(EXCEPTION_MSG + "400005");
			}

			// 超时
			if (validation) {
				throw new RuntimeException(EXCEPTION_MSG + "400007");
			}

		}

		return super.preHandle(request, response, handler);
	}
}

D。配置拦截器

/**  
* @Title: ResourceConfig.java 
* @Description:
* @Copyright: Copyright (c) 2018
* @Company:http://www.sinocon.cn
* @author Administrator  
* @date 2018年8月6日  
* @version 1.0  
*/
package cn.sinocon.hive.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import cn.sinocon.hive.interceptor.LoginInterceptor;

/**
 * @Title: ResourceConfig
 * @Description:
 * @author:Administrator
 * @date 2018年8月6日
 */
@Configuration
public class ResourceConfig implements WebMvcConfigurer {

	/*
	 * 默认首页的设置,当输入域名是可以自动跳转到默认指定的网页
	 * 
	 * <p>Title: addViewControllers</p>
	 * 
	 * <p>Description: </p>
	 * 
	 * @param registry
	 * 
	 * @see org.springframework.web.servlet.config.annotation.WebMvcConfigurer#
	 * addViewControllers(org.springframework.web.servlet.config.annotation.
	 * ViewControllerRegistry)
	 * 
	 */
	@Override
	public void addViewControllers(ViewControllerRegistry registry) {
		registry.addViewController("/").setViewName("forward:/index.html");
	}

	/* (non-Javadoc)  
	
	 * <p>Title: addInterceptors</p>  
	
	 * <p>Description: 拦截器配置</p>  
	
	 * @param registry  
	
	 * @see org.springframework.web.servlet.config.annotation.WebMvcConfigurer#addInterceptors(org.springframework.web.servlet.config.annotation.InterceptorRegistry)  
	
	 */
	@Override
	public void addInterceptors(InterceptorRegistry registry) {
		registry.addInterceptor(new LoginInterceptor()).addPathPatterns("/api/**");
		WebMvcConfigurer.super.addInterceptors(registry);
	}

}

E、在调用的controller方法中加上注解@RequireSignature

大功告成


打赏

分享到:

最近浏览
DengXianSheng Deng LV211月15日
星星星星
lx50034084 LV311月14日
星星星星星星
chenglei1314 LV311月14日
星星星星星星
rourou LV411月9日
月亮
江小鱼 LV1011月5日
月亮月亮星星星星
心死已无爱 LV310月25日
星星星星星星
郭亚鹏 LV1110月9日
月亮月亮星星星星星星
IT小牛 LV210月9日
星星星星
苏同学 LV1210月8日
月亮月亮月亮
学习天成 LV19月29日
星星
顶部客服微信二维码底部
>扫描二维码关注最代码为好友扫描二维码关注最代码为好友