2016-05-25 15:45:04 u012012240 阅读数 3408
  • 微服务架构下的分布式Session管理

    Session一直以来都是Web应用中不可或缺的一个组成部分,在Web应用架构的变迁与演进过程中,Session管理也在随之改变,不同架构下有着不同的Session管理实现。 而时下热门的微服务架构又会为Session管理带来哪些改变与影响,我们在做微服务架构下的Session管理时又该考虑哪些问题。在此我们将会从改变与影响出发,一起探讨如何做好微服务架构下的Session管理。

    4683 人正在学习 去看看 CSDN讲师

  主要用途:主要用于session管理。在微信开发时,因为通过微信服务器转发,而丢失cookie和seesion,无法使用session,所以使用微信服务器post过来的openid当做sessionid ,openid是一个唯一不重复的,这样每个用户的状态是可以独立的
参考地址:http://www.ablanxue.com/shtml/201502/26386_1.shtml
参考文章比较乱,但给我带来了灵感。我的这个和他的基础相同,不相同的是:
1.我采用一个线程来管理session集合,减少内存消耗,session失效时间可能会多一秒
2.我session采用session创建时间,session保存时间

package com.fkhd.weixin.session;
/**
 * session键值对实体类
 */
public class SessionData {
	/**
	 * 键
	 */
	private String name;
	/**
	 * 值
	 */
	private Object value;
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public Object getValue() {
		return value;
	}
	public void setValue(Object value) {
		this.value = value;
	}

}
package com.fkhd.weixin.session;

import java.util.ArrayList;
import java.util.List;

/**
 * session实体类
 */
public class Session {
	/**
	 * 唯一标识
	 */
	private String openid;
	/**
	 * 创建时间
	 */
	private long createTime;  
	/**
	 * 有效时间
	 */
	private long validTime;
	/**
	 * 键值对集合 
	 */
	private List<SessionData> sessionDatas=new ArrayList<SessionData>();
	
	public String getOpenid() {
		return openid;
	}
	public void setOpenid(String openid) {
		this.openid = openid;
	}
	public long getCreateTime() {
		return createTime;
	}
	public void setCreateTime(long createTime) {
		this.createTime = createTime;
	}
	public long getValidTime() {
		return validTime;
	}
	public void setValidTime(long validTime) {
		this.validTime = validTime;
	}
	public List<SessionData> getSessionDatas() {
		return this.sessionDatas;
		
	}
	public void setSessionDatas(List<SessionData> sessionDatas) {
		this.sessionDatas=sessionDatas;
		} 
}
package com.fkhd.weixin.session;

import java.util.ArrayList;
import java.util.List;

/**
 * session集合
 */
public class SessionList {
	/**
	 * session集合
	 */
	public static List<Session> sessionList=new ArrayList<Session>();
	/**
	 * 查找session中是否有指定项
	 * @param openid session标识
	 * @return 若存在openid相同的session,返回在集合中对应的位置,若不存在返回-1
	 */
	public static int search(String openid){
		int size = sessionList.size(); 
		for ( int i=0 ; i<size ; i++){ 
			if ( sessionList.get(i).getOpenid().equals(openid)) 
				return i;
		}
		return -1;           
	}
	/**
	 * 移除session
	 */
	public static void remove(String openid) {
		int i = search(openid);
		if (i>=0)
			sessionList.remove(i);
	}
	/**
	 * 向session中存入数据
	 * @param name
	 * @param value
	 * @param openid
	 */
	public static void setSeesion(String name, Object value, String openid){
		System.out.println("setSeesion " + name + " " + value + " " + openid);
		int i = search(openid);
		if (i>=0){
			SessionData data = new SessionData();
			data.setName(name);
			data.setValue(value);
			sessionList.get(i).getSessionDatas().add(data);
		}
	} 
	/**
	 * 创建session 
	 * @param openid
	 */
	public static void setSeesion(String openid){
		System.out.println("setSeesion " + openid);
		int i = search(openid); 
		if (i<0){ 
			//建立线程 
			Session session = new Session();
			//两个小时会话
			session.setValidTime(120*60*1000);
			//当前时间戳
			session.setCreateTime(System.currentTimeMillis());
			//session唯一标识
			session.setOpenid(openid);
			//添加session
			sessionList.add(session);
		}
	}
	/**
	 * 根据name,获取session对象中键值对
	 * @param name
	 * @param openid
	 */
	public static Object getSession(String name, String openid){
		System.out.println("getData " + name + " " + openid);
		int i = search(openid); 
		if (i>=0) {
			SessionData sessionData=null;
			for (SessionData sData : sessionList.get(i).getSessionDatas()) {
				if (sData.getName().equals(name)) {
					sessionData=sData;
				}
			}
			if (sessionData!=null){
				Object value = sessionData.getValue();
				System.out.println(name + "值为 " + value);
				return value;
			} 
		}
		return null;
	}
	/**
	 * 根据name,移除session对象中键值对
	 * @param name
	 * @param openid
	 */
	public static void removeData(String name, String openid){
		int i = search(openid); 
		if (i>0){
			sessionList.get(i).getSessionDatas().remove(name);
		}
	}
}
package com.fkhd.weixin.session;

import java.util.ArrayList;
import java.util.List;

/**
 * 更新session集合状态
 */
public class SessionThread implements Runnable{ 
	public void run() { 
		while(true){
			//当前时间戳
			long currentTime=System.currentTimeMillis();
			//休眠1分钟
			try {
				if (SessionList.sessionList.isEmpty()){ 
					System.out.println("现在还没session,休息10分钟!");
					//休眠10分钟
					Thread.sleep(10*60*1000);
				}else {
					//用来装需要删除的元素
					List<Session> delList = new ArrayList<Session>();
					//遍历session集合,将时间戳到期的提出
					for (Session session : SessionList.sessionList) {
						//当前时间大于创建时间加保存时间的session移除
						if (currentTime>session.getCreateTime()+session.getValidTime()) {
							delList.add(session);
							System.out.println("要移除的session,openid为:"+session.getOpenid());
						}
					}
					//移除要删除的元素
					SessionList.sessionList.removeAll(delList);
					//休眠1分钟
					Thread.sleep(60*1000);
				}
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
}


2015-10-13 15:58:44 sinat_22319877 阅读数 3227
  • 微服务架构下的分布式Session管理

    Session一直以来都是Web应用中不可或缺的一个组成部分,在Web应用架构的变迁与演进过程中,Session管理也在随之改变,不同架构下有着不同的Session管理实现。 而时下热门的微服务架构又会为Session管理带来哪些改变与影响,我们在做微服务架构下的Session管理时又该考虑哪些问题。在此我们将会从改变与影响出发,一起探讨如何做好微服务架构下的Session管理。

    4683 人正在学习 去看看 CSDN讲师

    session是存储在服务器端的,那么区别每个用户的session就需要使用客户端的cookie,微信服务器是不发送cookie到开发者服务器,所以基于cookie的session无法使用。

    但是只要为每个用户设置一个唯一的session_id,也可以达到同样的效果。

    每个人微信号是唯一的,所以我们可以使用微信号作为用户的session_id,也可以将其md5加密后使用。

    如下将ToUserName设置为session_id

  $postStr = $GLOBALS["HTTP_RAW_POST_DATA"];
  if(!empty($postStr)){
      $msg = (array)simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA);
      $postStr = $GLOBALS["HTTP_RAW_POST_DATA"];  if(!empty($postStr)){        
      $msg = (array)simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA);
      //设置session_id
      session_id($msg['ToUserName']);
      session_start();
  }






2017-11-30 17:52:07 tanglei6636 阅读数 33823
  • 微服务架构下的分布式Session管理

    Session一直以来都是Web应用中不可或缺的一个组成部分,在Web应用架构的变迁与演进过程中,Session管理也在随之改变,不同架构下有着不同的Session管理实现。 而时下热门的微服务架构又会为Session管理带来哪些改变与影响,我们在做微服务架构下的Session管理时又该考虑哪些问题。在此我们将会从改变与影响出发,一起探讨如何做好微服务架构下的Session管理。

    4683 人正在学习 去看看 CSDN讲师

在开发中我遇到了这样一个问题:

线上环境通过多台主机多实例部署,所以要用redis存储统一的session信息,然后通过一个uid为key来区分,当用户通过微信网页鉴权接口(整个接口的交互我写在了action里面)时会给用户分配一个uid然后以此uid为key存储对应session信息。

另外我的页面是通过angularjs实现的,所有的请求都是异步的,本来项目大部分功能已经实现,所以怎么传递uid成了问题,以往的的解决办法都是通过存cookie来实现的,所以我页面实现微信网页鉴权的页面将要使用的uid存到了cookie中,但是当使用时却取不到。

这里出现了一个非常蠢的错误,简单讲就是将微信公众号至主页面理解成了一个浏览器,这样没有经过浏览器就存cookie是无法实现的,如图:

错误方式

正确方式:
正确方式

用一个页面进行过渡,通过这个页面存cookie。

在这里插入图片描述

2018-11-04 17:13:23 Srain13 阅读数 241
  • 微服务架构下的分布式Session管理

    Session一直以来都是Web应用中不可或缺的一个组成部分,在Web应用架构的变迁与演进过程中,Session管理也在随之改变,不同架构下有着不同的Session管理实现。 而时下热门的微服务架构又会为Session管理带来哪些改变与影响,我们在做微服务架构下的Session管理时又该考虑哪些问题。在此我们将会从改变与影响出发,一起探讨如何做好微服务架构下的Session管理。

    4683 人正在学习 去看看 CSDN讲师

普通的Web开发,都是把sessionid保存在cookie中传递的。

不管是java还是php,服务端的会在response的header中加上Set-Cookie。

Response Headers
Content-Type:application/json;charset=UTF-8
Date:Mon, 02 Apr 2018 16:02:42 GMT
Set-Cookie:JSESSIONID=781C7F500DFA24D663BA243A4D9044BC;path=/yht;HttpOnly

浏览器的请求也会在header中加上

Request Headers
Accept:*/*
Accept-Encoding:gzip, deflate, br
Accept-Language:zh-CN,zh;q=0.8
Cache-Control:no-cache
Connection:keep-alive
Content-Length:564
content-type:application/json
Cookie:JSESSIONID=781C7F500DFA24D663BA243A4D9044BC;path=/yht;HttpOnly

通过这个sessionid就能使浏览器端和服务端保持会话,使浏览器端保持登录状态

但是,微信小程序不能保存Cookie,导致每次wx.request到服务端都会创建一个新的会话,小程序端就不能保持登录状态了

一个比较简单的办法就是在小程序端把cookie保存到storage里,后续请求的时候再读storage,把cookie添加到请求头里,这样做的好处就是,服务端不用做任何改动
具体操作如下:

把服务端response的Set-Cookie中的值保存到Storage中

wx.request({
    url: path,
    method:method,
    header: header,
    data:data,
    success:function(res){
        if(res && res.header && res.header['Set-Cookie']){
            wx.setStorageSync('cookieKey', res.header['Set-Cookie']);//保存Cookie到Storage
          }
},
    fail:fail
  })

wx.request再从Storage中取出Cookie,封装到header中

let cookie = wx.getStorageSync('cookieKey');
  let path=conf.baseurl+url;
  let header = { };
  if(cookie){
    header.Cookie=cookie;
  }
  
  wx.request({
    url: path,
    method:method,
    header: header,
    data:data,
    success:success,
    fail:fail
  })

原文作者:爱吐槽的coder
链接:https://www.jianshu.com/p/5c928e0df024

2016-01-14 13:55:49 HinstenyHisoka 阅读数 5925
  • 微服务架构下的分布式Session管理

    Session一直以来都是Web应用中不可或缺的一个组成部分,在Web应用架构的变迁与演进过程中,Session管理也在随之改变,不同架构下有着不同的Session管理实现。 而时下热门的微服务架构又会为Session管理带来哪些改变与影响,我们在做微服务架构下的Session管理时又该考虑哪些问题。在此我们将会从改变与影响出发,一起探讨如何做好微服务架构下的Session管理。

    4683 人正在学习 去看看 CSDN讲师

当我们进行微信平台相关内容开发时,如果想做一些针对微信用户进行一些信息获取及消息推送,那我们第一步先要拿到微信用户的OPENID(用户唯一标识,建立在一个公众号与一个微信用户之间,即针对一个公众号,所有的微信用户都享有一个独立不变的标识);


1 . 如果微信用户已经关注自己的公众号, 那可以通过微信接口里面的用户管理模板直接获取到对应公众号的所有已关注用户OPENID的列表;
1). 在调用所有的微信接口之前,我们先要请求授权,拿到access_token

    @Getter
    @Config("wechat.appid")
    private static String WECHATAPPID;

    @Getter
    @Config("wechat.appsecret")
    private static String WECHATAPPSECRET;

    public static final String ACCESS_TOKEN = "access_token";
    public final static String WECHATACCESSTOKEN = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=";
        /**
     * <p>
     * 成功: {"access_token":"ACCESS_TOKEN","expires_in":7200}
     * </p>
     * <p>
     * 失败: {"errcode":40013,"errmsg":"invalid appid"}
     * </p>
     * @return 如果没有获取到有效的凭据,就返回 null
     */
    private static Map getWechatAPPAccessTokenFromWeChat() {
        StringBuilder accessTokenUrl = new StringBuilder().append(WECHATACCESSTOKEN).append(WECHATAPPID)
                        .append("&secret=").append(WECHATAPPSECRET);
        Map result = HttpClientUtil.readHttpContent(accessTokenUrl.toString());
        if (result != null && result.get(ACCESS_TOKEN) != null)
            return result;
        return null;
    }

    //HttpClientUtil.class static method
    /**
     * get json content and transform to Map from the urlStr
     * If occuer one error just return null.
     * @param urlStr
     * @return
     */
    public static Map<String,Object> readHttpContent(String urlStr){
        Map<String, Object> result = null;
        HttpEntity httpEntity = null;
        try {
            HttpResponse response = Request.Get(urlStr).execute().returnResponse();
            httpEntity = response.getEntity();
        }catch (IOException e) {
            logger.error("Get resouse from {} had occurred an error: {}", urlStr, e.toString());
            return result;
        }
        try (
            InputStream in = httpEntity.getContent();
        ){
            ObjectMapper mapper = new ObjectMapper();
            result = mapper.readValue(in, HashMap.class);
        }catch (IOException e) {
            logger.error("Get json content from {} had occurred an error: {}", urlStr, e.toString());
            return result;
        }
        return result;
    }

2). 使用前面得到的access_token获取公众号的微信关注用户列表

    public static final String ERRMSG = "errmsg";
    public final static String WECHATUSERLISTURL = "https://api.weixin.qq.com/cgi-bin/user/get?access_token=";
    /**
     *<p>
     * 获取成功:{
     *  "total":23000,
     *  "count":10000,
     *  "data":{"
     *    openid":[
     *      "OPENID1",
     *      "OPENID2",
     *      ...,
     *      "OPENID10000"
     *    ]
     *  },
     *  "next_openid":"OPENID10000"
     *  }
     *</p>
     *
     *<p>
     * 获取失败:{"errcode":40013,"errmsg":"invalid appid"}
     *</p>
     *
     *
     * 获取微信公众帐号的关注者列表(wechat user openID),每次最多10000个
     *
     * @param accessToken
     * @param next_openid: 没有此参数,则默认从关注列表的第一个开始获取
     * @return
     */
    public static Map getWechatUserOpenIDList(String accessToken, String next_openid) {
        StringBuilder urlB = new StringBuilder().append(WECHATUSERLISTURL).append(accessToken);
        if (!Strings.isBlank(next_openid))
            urlB.append("&next_openid=").append(next_openid);
        Map result = HttpClientUtil.readHttpContent(urlB.toString());
        if (result != null && result.get(ERRMSG) == null)
            return result;
        return null;
    }
采用方法一,到此便得到了用户openID列表,可以进行后面获取微信用户信息及向用户推送模板消息等;

2 . 无论微信用户是否关注自己的公众号, 当微信用户访问我们的web站点时(通过微信内置浏览器),我们可以通过页面授权的方式拿到微信用户的OpendID(页面授权需要跳转到微信服务器拿到oauth_code然后再换取access_token及OpendID);
1). 为我们的web站点服务添加一个filter,拦截来自微信浏览器的请求,进行302跳转到微信站点,拿到授权码再回调回来;

public class WechatFilter implements Filter {

    private static final Logger logger             = LoggerFactory.getLogger(WechatFilter.class);

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {}

    @Override
    public void doFilter(ServletRequest request, ServletResponse response,
                         FilterChain chain) throws IOException, ServletException {
        HttpServletRequest httpRequest = (HttpServletRequest) request;

        if (response.isCommitted()) {
            chain.doFilter(request, response);
            return;
        }
        //判断请求是否来自微信浏览器
        if (!WebUtil.isWechatRequest(httpRequest)) {
            chain.doFilter(request, response);
            return;
        }
        String requestURL = WebUtil.getRequsetUrl(httpRequest, true, true);
        logger.debug("Request from wechat client:{}", requestURL);
        HttpSession session = httpRequest.getSession();
        String weChatOpenId = (String) session.getAttribute(WeChatManager.WECHATOPENID);
        if (weChatOpenId == null) {
            if (httpRequest.getParameter(WeChatManager.CODE) == null) {
                StringBuilder url = new StringBuilder("http://").append(WebUtil.getRequsetUrl(httpRequest, true, true));
                response.reset();
                ((HttpServletResponse) response).sendRedirect(url.toString());
                return;
            } else {
                String oauthCode = httpRequest.getParameter(WeChatManager.CODE);
                if (!Strings.isBlank(oauthCode)) {
                    Map<String, Object> oauth_data = WeChatPageInterface.getWechatPageAccessToken(oauthCode);
                    weChatOpenId = oauthCode != null && oauth_data.get(WeChatManager.OPENID) != null ? (String) oauth_data.get(WeChatManager.OPENID) : null;
                    session.setAttribute(WeChatManager.WECHATOPENID, weChatOpenId);
                } else {
                    logger.warn("The oauth_code is empty!");
                }
            }
        }
        chain.doFilter(request, response);
    }

    @Override
    public void destroy() {
    }

}
    //WeChatPageInterface.class static method
    /**
     * <p>
     * 微信获取授权成功:{ "access_token":"ACCESS_TOKEN", "expires_in":7200, "refresh_token":"REFRESH_TOKEN",
     * "openid":"OPENID", "scope":"SCOPE", "unionid": "o6_bmasdasdsad6_2sgVt7hMZOPfL" }
     * </p>
     * <p>
     * 微信获取授权失败:{"errcode":40029,"errmsg":"invalid code"}
     * </p>
     *
     * 请求微信公众号的网页授权,得到用户OpenID和access_token
     * 微信网页授权是通过OAuth2.0机制实现的,在用户授权给公众号后,公众号可以获取到一个网页授权特有的接口调用凭证( 网页授权access_token),
     * 通过网页授权access_token可以进行授权后接口调用,如获取用户基本信息;
     *
     * @return 如果没有获取到有效的凭据,就返回 null
     */
    public static Map<String, Object> getWechatPageAccessToken(String oauthCode) {
        StringBuilder accessTokenUrl = new StringBuilder().append(WeChatManager.weChatAuthAccessTokenPage).append(WeChatManager.getWECHATAPPID())
                        .append("&secret=").append(WeChatManager.getWECHATAPPSECRET()).append("&code=")
                        .append(oauthCode).append("&grant_type=").append("authorization_code");
        Map<String, Object> accessToken = HttpClientUtil.readHttpContent(accessTokenUrl.toString());
        if (accessToken != null && accessToken.get(WeChatManager.ACCESS_TOKEN) != null) {
            return accessToken;
        }
        return null;
    }

至此,我们通过两种方式都可以获取微信用户的OpenID了,至于采用哪一种可以根据实际业务环境;

PS: OpenID是介于微信用户与公众账号之间恒定不变的,所以可以进行三方持久化存储;

没有更多推荐了,返回首页