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

本文详细阐述了HTTP请求中无状态特性的解决方案——Token与Session机制。解释了两者的概念与工作原理,并提供了具体的实现代码示例,帮助读者理解如何在实际应用中使用这两种机制来维护用户的会话状态。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


传统的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<String, AccessToken> map = new ConcurrentHashMap<String, AccessToken>();


	/**
	 * 生成加密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);
	}


}


### 微信小程序调用登录接口示例教程 在微信小程序开发过程中,为了实现用户的认证功能,通常会使用微信提供的登录接口。通过该接口可以获取到临登录凭证 code,并将其发送给服务器端换取 session_key openid。 #### 获取用户登录凭证 (code) 当用户打开小程序,可以通过 `wx.login` 方法来获得用户的登录状态以及拉取用户信息所需的 code: ```javascript wx.login({ success(res) { if (res.code) { console.log('Login Code:', res.code); // 将 code 发送到开发者服务器进行处理 } else { console.error('Failed to get login information:', res.errMsg); } }, }); ``` 此方法返回的数据包中包含了用于换取 session 的临票据 code[^1]。 #### 向开发者服务器请求数据 一旦获得了 code,则需要向自己的 PHP 或其他语言编写的后端服务发起 HTTP 请求,传递这个 code 参数以便于后续操作。这里以 GET 方式为例说明如何构建这样的网络请求: ```javascript const app = getApp(); // 假设这是你的服务器地址 const serverUrl = 'https://yourserver.com/login'; wx.request({ url: serverUrl, method: 'GET', data: { js_code: res.code }, // 把刚才得到的 code 放在这里面传过去 header: { 'content-type': 'application/json' // 默认值 }, success(response) { const respData = response.data; // 处理从服务器接收到的数据... console.log(respData); // 可能还需要保存某些重要信息比如 token 到本地存储里边去供之后使用 wx.setStorageSync('token', respData.token || ''); // 更新全局变量中的 userInfo 字段 app.globalData.userInfo = respData.userinfo; }, fail(error) { console.error('Request failed:', error); } }); ``` 上述代码片段展示了怎样利用 JavaScript 中的 `wx.request()` 函数来进行跨域 AJAX 调用来交换必要的身份验证令牌其他相关信息[^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值