java接口安全性与用户登录状态验证

在一个安卓App项目中访问java后台接口,对于请求接口安全性跟用户登录状态验证.接口安全性采用签名的方式用户登录状态采用token的方式具体实现思路如下(小小菜鸟欢迎指正)

1.java后台安全性配置:secret(密钥),appId(客户端标识),signParam(签名参数account_token),timeOut(超时时间)

2.设置拦截器,拦截器主要定义五个功能

  1.   作用一:设置字符编码(所用请求均为post请求)
  2. 作用二:分析url,url中包含"api"的请求参数中必须带有appid,account_token否则接口调用是吧
  3. 作用三:签名验证,签名主要是由appid,secret,versionCode 经md5加密生成的一个字符串
  4. 作用四:根据时间戳判断接口失效时间,在后台定义一个失效时间,客户端请求时带有时间戳
  5. 作用五:包含"user"字符串的url需要验证用户的登录状态,并重置redis用户:account_token状态
  6. 作用六:验证客户端版本提示客户端升级

签名比对的方法:客户端跟服务端都用同一个密钥,客户端请求后台接口时会根据account_toke+appId+版本号+密钥MD5加密一个签名字符串,后台按照同样的方法生成一个加密字符串,两个签名字符串进行比对相等才可以调用接口

3.用户登录功能:在用户登录成功后,用uuid随机生成一个字符串,userId,account_token相互对应设置时长,存入到redis中,并返回给客户端用户登录状态都要根据这account_token进行判断

总结:客户端请求必须携带的参数:appid,account_token,sign,timestamp

拦截器代码:

public class ApiSignVerityInterceptor implements HandlerInterceptor {

	private String secret;//密钥
	private String appid;
	private String sginParam;//account_token
	private String appidParam;
	private int timeOut;//超时时间
	private RedisUtil redis;

	public ApiSignVerityInterceptor(String secret, String appid,
			String sginParam, String appidParam, int timeOut, RedisUtil redis) {
		super();
		this.secret = secret;
		this.appid = appid;
		this.sginParam = sginParam;
		this.appidParam = appidParam;
		this.timeOut = timeOut;
		this.redis = redis;
	}

	@Override
	public boolean preHandle(HttpServletRequest request,
			HttpServletResponse response, Object handler) throws Exception {
		request.setCharacterEncoding("UTF-8");
		String url = request.getRequestURI();
		String contextPath = request.getContextPath();
		url = url.replace(contextPath, "");
        //包含api的请求必须带有sginParam,appidParam,timestamp
		if (url.contains("/api")) {
			MessageResult result = new MessageResult();
			String sgin = request.getParameter(sginParam);
			String appidStr = request.getParameter(appidParam);
			String timestamp = request.getParameter("timestamp");
			if (!appid.equals(appidStr)) {
				result.setErrorMsg("规则请求,接口结构不完整");
				ResourceUtil.writeJSON(result, response);
				return false;
			}
            //验证签名
			String urlApiSgin = ResourceUtil.getUrlApiSgin(request, sginParam, appidParam, secret);
			if (!urlApiSgin.equals(sgin)) {
				result.setErrorMsg("规则请求,接口签名错误");
				ResourceUtil.writeJSON(result, response);
				return false;
			}
            //验证接口时效
			if ("".equals(timestamp) || timestamp == null) {
				timestamp = String.valueOf(System.currentTimeMillis() + timeOut
						* 2 * 60 * 1000);
			}
			long nowTime = System.currentTimeMillis();
			long apiTime = new Long(timestamp) + timeOut * 60 * 1000;
			if (nowTime > apiTime) {
				result.setErrorMsg("规则请求,接口无效");
				ResourceUtil.writeJSON(result, response);
				return false;
			}
            //验证版本
			String reqVersionCode = request.getHeader(ResourceUtil.VERSION);
			Object version = redis.get(ResourceUtil.VERSION); 	//value: { versionCode: "1", title: "ios13", detail: "1\1\2\34\4", url: 'http://xxxxx/xxxx/xxx.apk'}
			if(version != null){
				JSONObject versionJSON = (JSONObject)version;
				Integer versionCode = versionJSON.getInteger("versionCode");
				if (versionCode != null) {
					if (versionCode > Integer.parseInt(reqVersionCode)) {
						result.setErrorMsg("APP版本需升级");
						result.setVersion(false);
						result.setVersionDetail(versionJSON);
						ResourceUtil.writeJSON(result, response);
						return false;
					}
				}
			}
			//用户登录状态
			if (url.contains("/user")) {
				String account_token = request.getHeader(ResourceUtil.ACCOUNT_TOKEN);
				boolean hasKey = redis.hasKey(account_token);
				if (!hasKey) {
					result.setLogin(false);
					result.setErrorMsg("用户登录失效,请重新登录");
					ResourceUtil.writeJSON(result, response);
					return false;
				}
				redis.expire(account_token, ResourceUtil.EXPIRES_IN);
			}
		}
		return true;
	}

	@Override
	public void postHandle(HttpServletRequest request,
			HttpServletResponse response, Object handler,
			ModelAndView modelAndView) throws Exception {

	}

	@Override
	public void afterCompletion(HttpServletRequest request,
			HttpServletResponse response, Object handler, Exception ex)
			throws Exception {

	}

}

生成签名代码:

public class ResourceUtil {
	
	public static final String ACCOUNT_TOKEN = "account_token";
	public static final String VERSION = "version";
	public static final Integer EXPIRES_IN = 60 * 60 * 24 * 7;
	public static final String PERMIT = "company/licens/";
	public static final String DETIL_IMAGE = "commodity/detail/";
	public static final String COVER = "commodity/cover/";
	public static final String DEMAND_IMAGE = "demand/detail/";
	public static final String DEMAND_COVER = "demand/cover/";
	public static final String USERFACE = "face/user";
	public static final String USER_COVER = "user/cover/";
	public static final String USER_TEMPLET = "user/templet/";

	
	
	public static void writeJSON(Object result, HttpServletResponse response)
			throws IOException {
		String value = JSON.toJSONString(result,
				SerializerFeature.BrowserCompatible,
				SerializerFeature.WriteClassName,
				SerializerFeature.DisableCircularReferenceDetect);
		response.setContentType("text/html");
		response.setCharacterEncoding("UTF-8");
		response.getWriter().print(value);
	}

	public static String getUrlApiSgin(HttpServletRequest request,String sginParam, String appidParam, String secret) {
		String sgin = null;
		Enumeration<String> parameterNames = request.getParameterNames();
		Map<String, String> values = new Hashtable<String, String>();
		while (parameterNames.hasMoreElements()) {
			String paramName = (String) parameterNames.nextElement();
			if (!sginParam.equals(paramName) && !appidParam.equals(paramName)
					&& !ACCOUNT_TOKEN.equals(paramName)) {
				String value = request.getParameter(paramName);
				values.put(paramName, value);
			}
		}
		if (values.size() > 0) {
			List<Map.Entry<String, String>> list = new ArrayList<Map.Entry<String, String>>(
					values.entrySet());
			Collections.sort(list, new Comparator<Map.Entry<String, String>>() {

				@Override
				public int compare(Entry<String, String> o1,
						Entry<String, String> o2) {
					return (o1.getKey().compareTo(o2.getKey()));
				}
			});
			String sginStr = secret;
			for (Entry<String, String> entry : list) {
				sginStr += entry.getKey() + entry.getValue();
			}
			sginStr += ResourceUtil.VERSION + request.getHeader(ResourceUtil.VERSION).toString();
			sgin = MD5Util.MD5(sginStr).toUpperCase();
		}
		return sgin;
	}
}

 登录功能代码:

if (login != null) {
					int status = Integer.parseInt(login.get("status")
							.toString());
					if (status != -1) {
						String user_id = login.get("user_id").toString();
						Object token_exists = redis.get(user_id);
						if (token_exists != null) {
							redis.del(token_exists.toString());
						}
						String account_token = UUIDUtils.getEncryUUID();
						TokenModel model = new TokenModel(user_id, account_token,
								ResourceUtil.EXPIRES_IN);
						redis.set(account_token, getContext(model),
								ResourceUtil.EXPIRES_IN);
						redis.set(user_id,
								account_token, ResourceUtil.EXPIRES_IN);
						result.setModel(model);
						result.getResult().put("account", login.get("account").toString());
						result.commit();
					} else {
						result.setErrorMsg("账号违规操作,已被禁用!");
					}
				} 

拦截器配置:

@Configuration
public class WebInitConfig extends WebMvcConfigurerAdapter {
	
	@Value("${frms.zbpls.api.secret}")
	private String secret;
	@Value("${frms.zbpls.api.appid:123456}")
	private String appid;
	@Value("${frms.zbpls.api.sginParam:sgin}")
	private String sginParam;
	@Value("${frms.zbpls.api.appidParam:appid}")
	private String appidParam;
	@Value("${frms.zbpls.api.timeOut: 1}")
	private int timeOut;
	@Autowired
	@Qualifier("systemRedis")
	private RedisTemplate<String, Object> redisSystemTemplate;

	@Override
    public void addInterceptors(InterceptorRegistry registry) {
	/*	RedisUtil redis = new RedisUtil(redisSystemTemplate);
        registry.addInterceptor(new ApiSignVerityInterceptor(secret, appid,
    			sginParam, appidParam, timeOut, redis)).addPathPatterns("/api/**");
        super.addInterceptors(registry);*/
    }
}
发布了10 篇原创文章 · 获赞 6 · 访问量 184

猜你喜欢

转载自blog.csdn.net/xzx19930928/article/details/89290695