关于app接口调用时如何使session和token票据来保证接口调用的安全


传统的http请求时无状态的。及一个用户向服务器端发送请求时,当再次发送请求时不能判断是同一个用户发送 的请求,及无法记录用户信息。

Session是指一个终端用户与交互系统进行通信的时间间隔,通常指从注册进入系统到注销退出系统之间所经过的时间也成为会话时间。

token是有后端加密产生的一段加密字符。app访问接口是的票据。

1.关于token域session之间的关联关系
 1-1.token和session必须存在否则接口无法调用
 1-1  1.用户A登陆--客户端A
 1-1  2.用户A 登陆---客户端B时 1.首先判断内存中是否存在token(通过账号名称查找)
         存在:给客户端B一个提示信息 --然后发送一个请求清空客户端A产生的session和token,
         然后重新登陆,产生新的token。
         不存在:直接登陆没有二次回调请求过程。
 1.当用户请求接口是:逻辑判断如下
   1.从session中取出当前会话的对象 ---
       会话存在 -
          1.解析token获取获取accesstoken对象
          2.判断当合会话的sessionid和内存中的session是否一致
          3.判断当前的accesstoken是否失效
          4.判断会话中的用户和当前的accesstoken是否一致。       

         5.当以上条件都满足是,在进行权限判断是否有权限调用当前请求的资源。

代码如下

   1.Accesstoken类如下

/**
 * app票据访问的凭证
 * @author Administrator
 *
 */
public class AccessToken {
	private String signature;// 签名  
    
    private String timestamp;// 时间戳  
    /*sessionid*/  
    private String sessionId;
    private String random;// 随机数  

	public String getRandom() {
		return random;
	}

	public void setRandom(String random) {
		this.random = random;
	}

	public String getSignature() {
		return signature;
	}

	public void setSignature(String signature) {
		this.signature = signature;
	}

	public String getTimestamp() {
		return timestamp;
	}

	public void setTimestamp(String timestamp) {
		this.timestamp = timestamp;
	}

	public String getSessionId() {
		return sessionId;
	}

	public void setSessionId(String sessionId) {
		this.sessionId = sessionId;
	}

	@Override
	public String toString() {
		return "signature="+signature+"×tamp="+timestamp+"&sessionId="+sessionId+"&random="+random;
	}
    
}
2TokenUtill工具如下

public class TokenUtil {
	private static ConcurrentHashMap map = new ConcurrentHashMap();


	/**
	 * 生成加密Token
	 * 
	 * @param username
	 * @return
	 */
	public static AccessToken generateAccessToken(String username,
			String sessionId) {
		AccessToken accessToken = new AccessToken();
		// 设置签名
		accessToken.setSignature(username);
		// 设置时间戳
		accessToken.setTimestamp(getTimeStamp());
		// 设置sessionId
		accessToken.setSessionId(sessionId);
		// 设置授权码
		accessToken.setRandom(getRandom());
		return accessToken;
	}


	/**
	 * 【重新生成】更新Token
	 * 
	 * @param token
	 * @return
	 */
	public static String reCreateToken(String token) {


		return null;
	}


	/**
	 * 设置token 
	 * @param username
	 * @param userToken
	 */
	public static void putToken(String username, AccessToken accessToken) {
		map.put(username, accessToken);
	}


	/**
	 * 判定是否已经登录
	 * 
	 * @param signature
	 * @return
	 */
	public static boolean hasLogin(String signature) {
		if (map.containsKey(signature)) {
			return true;
		}
		return false;
	}
	/**
	 * 清空token
	 * 
	 * @param signature
	 * @return
	 */
	public static void removeToken(String signature) {
		 map.remove(signature);
	}
	/**
	 * 获取accesstoken对象
	 * 
	 * @param signature
	 * @return
	 */
	public static AccessToken getAccessToken(String signature) {
		return map.get(signature);


	}


	/**
	 * 加密签名
	 * 
	 * @param encrypt
	 * @return
	 * @throws Exception
	 */
	public static String encryptSignature(AccessToken token)
			throws Exception {
		return PBECoder.encrypt(token.toString(), PBECoder.PWD, PBECoder.salt);
	}


	/**
	 * 加密签名
	 * 
	 * @param encrypt
	 * @return
	 * @throws Exception
	 */
	public static String encryptSignature(String username, String sessionId,
			byte[] salt) throws Exception {
		AccessToken token = generateAccessToken(username, sessionId);
		return PBECoder.encrypt(token.toString(), PBECoder.PWD, salt);
	}


	/**
	 * 解密签名
	 * 
	 * @param signature
	 * @return
	 * @throws Exception
	 */
	public static String decryptSignature(String encryptToken, byte[] salt)
			throws Exception {
		return PBECoder.decryptString(encryptToken, PBECoder.PWD, salt);
	}


	/**
	 * 解密签名
	 * 
	 * @param signature
	 * @return
	 * @throws Exception
	 */
	public static String decryptSignature(String encryptToken) throws Exception {
		return PBECoder
				.decryptString(encryptToken, PBECoder.PWD, PBECoder.salt);
	}


	/**
	 * 生成时间戳
	 * 
	 * @return
	 */
	public static String getTimeStamp() {
		return Calendar.getInstance().getTimeInMillis() + "";
	}


	/**
	 * 生成随机数
	 * 
	 * @return
	 */
	public static String getRandom() {
		return new Random().nextInt(999999999) + "";
	}


	/**
	 * 解析加密用户Token
	 * 
	 * @param token
	 * @return
	 * @throws Exception
	 */
	public static AccessToken decryptToken(String encyptToken) throws Exception {


		String[] params = decryptSignature(encyptToken).split("&");
		// 分析用户提交过来的Token
		AccessToken accessToken = new AccessToken();
		for (int i = 0, j = params.length; i < j; i++) {
			String[] currentParams = params[i].split("=");
			String param = currentParams[0];
			if ("signature".equals(param)) {
				accessToken.setSignature(currentParams[1]);
			} else if ("timestamp".equals(param)) {
				accessToken.setTimestamp(currentParams[1]);
			} else if ("sessionId".equals(param)) {
				accessToken.setSessionId(currentParams[1]);
			}
		}
		return accessToken;
	}


	/**
	 * 验证token的失效性
	 * 
	 * @param timestampStr
	 *            时间戳
	 * @return
	 * @throws Exception
	 */
	public static boolean verifyAccessToken(String timestampStr)
			throws Exception {
		// 验证是否存在此用户登录的Token
		if (timestampStr != null) {
			// 判定时间戳是否过期
			long currentTime = Calendar.getInstance().getTimeInMillis();
			long timestamp = Long.valueOf(timestampStr);
			// Token有效时间为60分钟
			long verifyTime = 60 * 60 * 1000;
			if (currentTime - timestamp > verifyTime) {
				return true;
			}
			return false;
		}
		return true;
	}

3.在拦截器中拦截调用接口的请求

/**
 * 跨域拦截器 --主要功能 获取会话信息和token--通过token解析成accesstoken对象--对用户进行权限判断以及token的失效性判断
 * 
 * @author wjb
 * @version 1.0.0 2016.8
 */


public class CrossInterceptor extends HandlerInterceptorAdapter {
	@Override
	public boolean preHandle(HttpServletRequest request,HttpServletResponse response, Object handler) throws Exception {
		String token = ""; /* app接口票据 */
		String errcode ="0" ; /*返回的状态码*/
		String errmsg = ""; /* 接口调用失败是返回给客户端的提示信息 */
		Enumeration e = request.getHeaders("X-CSRF-TOKEN");/* 从请求的header中获取token */
		while (e.hasMoreElements()) {
			token = (String) e.nextElement();
		}
		HttpSession session =  request.getSession();
		Object temp = session.getAttribute(ProperHelper.SESSION_KEY);/* 获取当前会话的用户 */
		/* token字符串解析成accesstoken对象对该对象进行验证 */
		if (token != null && !token.isEmpty() && temp != null) {
			Member member =(Member)temp;
			token = java.net.URLDecoder.decode(token, "UTF-8");
			AccessToken accessToken = TokenUtil.decryptToken(token);
			// 验证是否存在此用户登录的Token
			if (accessToken != null) {
				/*session是否一致*/
				if(!TokenUtil.getAccessToken(accessToken.getSignature()).getSessionId().equals(accessToken.getSessionId())){
					errcode="113";
					errmsg ="session异常请重新登陆";
				}// 判定时间戳是否过期,
				else if (TokenUtil.verifyAccessToken(accessToken.getTimestamp())) {
					errcode="111";
					errmsg = "token已失效,请重新登陆";
				}
				/* 验证登陆的用户和token解析出来的用户是否一致 */
				else if (!accessToken.getSignature().equals(member.getLoginName())) {
					errcode ="112";
					errmsg = "token被篡改";
				} else {
					/* token验证通过后下一步1.对请求访问的地址进行权限拦截 */
				}
			} else {
				errcode ="114";
				errmsg = "无效的token";
			}
		} else {
			errcode ="114";
			errmsg = "session失效";
		}
		if (!errmsg.isEmpty()) {
			/*异常时清空session*/
			session.invalidate();
		   JsonUtils.response(errcode ,errmsg, request, response);
			return false;
		}
		return super.preHandle(request, response, handler);
	}


}


你可能感兴趣的:(ionic)