springboot利用springsession实现redis共享session,并且自定义sessionid

来源:互联网 发布:漫画控不能连接网络 编辑:程序博客网 时间:2024/06/01 07:55

一、初衷

作为一个刚入行的小白,在公司接手了一个springboot的项目,项目中我们完全使用session,管理用户的登陆信息及权限,项目完成时,我们需要集成某公司的nigix服务器,甲方要求,并且由甲方提供,一开始,我们接入之后,发现session丢失,就是nigix每次转发过来的请求其中cookie都会丢失,这个问题我们折腾了好多天,没有解决,后来发现他们的nigix服务器就是不能带cookie过来,因为集成了统一身份认证,没次请求都会带一个固定的请求头,并且请求头里面的数据会最为用户的id,所以考虑以下,决定用springsession来管理HTTPsession,保存到redis服务器中,实现session共享,并且用用户的id来做为中间件,在redis中管理session,从而间接的实现自定义sessionid。

二、干货

1.引用依赖
 <dependency>
      <groupId>org.springframework.session</groupId>
      <artifactId>spring-session</artifactId>
</dependency>
<dependency>  
    <groupId>org.springframework.boot</groupId>  
    <artifactId>spring-boot-starter-data-redis</artifactId>  
</dependency>

      2. 在配置文件application.properties中加入参数
# database namespring.redis.database=0# server host1spring.redis.host=**********    //redis服务器ip# server password#spring.redis.password=            //redis服务器密码   默认是没有的#connection portspring.redis.port=6379      //redis服务器端口号# pool settings ...spring.redis.pool.max-idle=8spring.redis.pool.min-idle=0spring.redis.pool.max-active=8spring.redis.pool.max-wait=-1# name of Redis server#spring.redis.sentinel.master=# comma-separated list of host:port pairs#spring.redis.sentinel.nodes=spring.session.store-type=redis



3.在springboot启动文件类上加入注解

@EnableRedisHttpSession(maxInactiveIntervalInSeconds = 1200,redisNamespace="xxxx")
  其中1200redis中session过期时间


并在启动文件中注入 session策略    这个策略是我自己实现的    
  @Bean
 public HttpSessionStrategy httpSessionStrategy() { 

// return new MyHeaderHttpSessionStrategy();   //  这是原来的策略  HeaderHttpSessionStrategy()

return new MyCookieHttpSessionStrategy(); //   这是原来的策略    CookieHttpSessionStrategy();
//


 }

4.MyHeaderHttpSessionStrategy:

package com.taibang.logisticsmanager.config;import java.util.concurrent.TimeUnit;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.data.redis.core.RedisTemplate;import org.springframework.data.redis.core.ValueOperations;import org.springframework.session.Session;import org.springframework.session.web.http.HttpSessionStrategy;import org.springframework.util.Assert;/** * 自方法只能从请求头中读取sessionid * 请求头中的信息作为中间件 * @author Dream * */public class MyHeaderHttpSessionStrategy implements HttpSessionStrategy { @Autowiredprivate RedisTemplate redisTemplate;private String headerName = "CAS_USER";//每次请求的请求头名称 private String defaultSessionId = "default-sessionid";public String getRequestedSessionId(HttpServletRequest request) {System.out.println("测试CAS_USER:"+request.getHeader("CAS_USER"));ValueOperations<String, String> vops = redisTemplate.opsForValue();System.out.println("vops1:"+vops);String sessionid = request.getHeader("CAS_USER");System.out.println("redis中sessionid"+sessionid); if (sessionid != null && !sessionid.equals("")) {            String jsessionid = vops.get(sessionid);            if(jsessionid!=null){               boolean flag =redisTemplate.expire(sessionid, 60*10, TimeUnit.SECONDS);               System.out.println("flag:"+flag);            }            return jsessionid;        } else {        System.out.println("返回默认sessionid"+sessionid);            return vops.get(sessionid);        }}public void onNewSession(Session session, HttpServletRequest request,HttpServletResponse response) {String jsessionid = request.getHeader("CAS_USER");String sessionid = session.getId();System.out.println("创建sessionid:"+sessionid);        ValueOperations<String, String> vops = redisTemplate.opsForValue();        System.out.println("创建sessionvops2:"+vops);        if (jsessionid != null && !jsessionid.equals("")) {            //保存xxx和sessionid映射关系            vops.set(jsessionid, sessionid);            boolean flag= redisTemplate.expire(jsessionid, 60*10, TimeUnit.SECONDS);            System.out.println("flag:"+flag);        }else{            //没有传xxx时,保存为默认            vops.set(jsessionid, sessionid);            redisTemplate.expire(jsessionid, 60*10, TimeUnit.SECONDS);        }response.setHeader(this.headerName, jsessionid);}public void onInvalidateSession(HttpServletRequest request,HttpServletResponse response) {System.out.println("注销session");String jsessionid = request.getHeader("CAS_USER");     redisTemplate.expire(jsessionid, 0, TimeUnit.SECONDS);response.setHeader(this.headerName, "");}/** * The name of the header to obtain the session id from. Default is "x-auth-token". * * @param headerName the name of the header to obtain the session id from. */public void setHeaderName(String headerName) {Assert.notNull(headerName, "headerName cannot be null");this.headerName = headerName;}}




5: MyCookieHttpSessionStrategy();因为项目需要两个登陆入口,如果不使用nigix服务器,没有了请求头,就没有办法管理,所有就写了这个  保留了cookie的机制
package com.taibang.logisticsmanager.config;import java.io.UnsupportedEncodingException;import java.net.URLEncoder;import java.util.HashSet;import java.util.LinkedHashMap;import java.util.List;import java.util.Map;import java.util.Set;import java.util.StringTokenizer;import java.util.concurrent.TimeUnit;import java.util.regex.Matcher;import java.util.regex.Pattern;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import javax.servlet.http.HttpServletResponseWrapper;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.data.redis.core.RedisTemplate;import org.springframework.data.redis.core.ValueOperations;import org.springframework.session.Session;import org.springframework.session.web.http.CookieHttpSessionStrategy;import org.springframework.session.web.http.CookieSerializer;import org.springframework.session.web.http.CookieSerializer.CookieValue;import org.springframework.session.web.http.DefaultCookieSerializer;import org.springframework.session.web.http.HttpSessionManager;import org.springframework.session.web.http.MultiHttpSessionStrategy;import org.springframework.util.Assert;/** * 自定义策略,有请求头,从请求头读取sessionid信息,无请求头使用cookie的策略 * @author Dream * */public final class MyCookieHttpSessionStrategy implements MultiHttpSessionStrategy, HttpSessionManager{/** * The default delimiter for both serialization and deserialization. */private static final String DEFAULT_DELIMITER = " ";private static final String SESSION_IDS_WRITTEN_ATTR = MyCookieHttpSessionStrategy.class.getName().concat(".SESSIONS_WRITTEN_ATTR");static final String DEFAULT_ALIAS = "0";static final String DEFAULT_SESSION_ALIAS_PARAM_NAME = "_s";private static final Pattern ALIAS_PATTERN = Pattern.compile("^[\\w-]{1,50}$");private String sessionParam = DEFAULT_SESSION_ALIAS_PARAM_NAME;private CookieSerializer cookieSerializer = new DefaultCookieSerializer();/** * The delimiter between a session alias and a session id when reading a cookie value. * The default value is " ". */private String deserializationDelimiter = DEFAULT_DELIMITER;/** * The delimiter between a session alias and a session id when writing a cookie value. * The default is " ". */private String serializationDelimiter = DEFAULT_DELIMITER;private String headerName = "CAS_USER";//此处请求头名成可修改@Autowiredprivate RedisTemplate redisTemplate;public String getRequestedSessionId(HttpServletRequest request) {ValueOperations<String, String> vops = redisTemplate.opsForValue();String sessionid = request.getHeader("CAS_USER"); if (sessionid != null && !sessionid.equals("")) {            String jsessionid = vops.get(sessionid);            if(jsessionid!=null){               boolean flag =redisTemplate.expire(sessionid, 60*10, TimeUnit.SECONDS);            }            return jsessionid;        }Map<String, String> sessionIds = getSessionIds(request);String sessionAlias = getCurrentSessionAlias(request);return sessionIds.get(sessionAlias);}public String getCurrentSessionAlias(HttpServletRequest request) {if (this.sessionParam == null) {return DEFAULT_ALIAS;}String u = request.getParameter(this.sessionParam);if (u == null) {return DEFAULT_ALIAS;}if (!ALIAS_PATTERN.matcher(u).matches()) {return DEFAULT_ALIAS;}return u;}public String getNewSessionAlias(HttpServletRequest request) {Set<String> sessionAliases = getSessionIds(request).keySet();if (sessionAliases.isEmpty()) {return DEFAULT_ALIAS;}long lastAlias = Long.decode(DEFAULT_ALIAS);for (String alias : sessionAliases) {long selectedAlias = safeParse(alias);if (selectedAlias > lastAlias) {lastAlias = selectedAlias;}}return Long.toHexString(lastAlias + 1);}private long safeParse(String hex) {try {return Long.decode("0x" + hex);}catch (NumberFormatException notNumber) {return 0;}}public void onNewSession(Session session, HttpServletRequest request,HttpServletResponse response) {String jsessionid = request.getHeader("CAS_USER");String sessionid = session.getId();        ValueOperations<String, String> vops = redisTemplate.opsForValue();        if (jsessionid != null && !jsessionid.equals("")) {            //保存xxx和sessionid映射关系            vops.set(jsessionid, sessionid);            boolean flag= redisTemplate.expire(jsessionid, 60*10, TimeUnit.SECONDS);            return;        }Set<String> sessionIdsWritten = getSessionIdsWritten(request);if (sessionIdsWritten.contains(session.getId())) {return;}sessionIdsWritten.add(session.getId());Map<String, String> sessionIds = getSessionIds(request);String sessionAlias = getCurrentSessionAlias(request);sessionIds.put(sessionAlias, session.getId());String cookieValue = createSessionCookieValue(sessionIds);this.cookieSerializer.writeCookieValue(new CookieValue(request, response, cookieValue));}@SuppressWarnings("unchecked")private Set<String> getSessionIdsWritten(HttpServletRequest request) {Set<String> sessionsWritten = (Set<String>) request.getAttribute(SESSION_IDS_WRITTEN_ATTR);if (sessionsWritten == null) {sessionsWritten = new HashSet<String>();request.setAttribute(SESSION_IDS_WRITTEN_ATTR, sessionsWritten);}return sessionsWritten;}private String createSessionCookieValue(Map<String, String> sessionIds) {if (sessionIds.isEmpty()) {return "";}if (sessionIds.size() == 1 && sessionIds.keySet().contains(DEFAULT_ALIAS)) {return sessionIds.values().iterator().next();}StringBuffer buffer = new StringBuffer();for (Map.Entry<String, String> entry : sessionIds.entrySet()) {String alias = entry.getKey();String id = entry.getValue();buffer.append(alias);buffer.append(this.serializationDelimiter);buffer.append(id);buffer.append(this.serializationDelimiter);}buffer.deleteCharAt(buffer.length() - 1);return buffer.toString();}public void onInvalidateSession(HttpServletRequest request,HttpServletResponse response) {if(request.getHeader("CAS_USER")!=null) {String jsessionid = request.getHeader("CAS_USER");    redisTemplate.expire(jsessionid, 0, TimeUnit.SECONDS);}else {Map<String, String> sessionIds = getSessionIds(request);String requestedAlias = getCurrentSessionAlias(request);sessionIds.remove(requestedAlias);String cookieValue = createSessionCookieValue(sessionIds);this.cookieSerializer.writeCookieValue(new CookieValue(request, response, cookieValue));}}public void setHeaderName(String headerName) {Assert.notNull(headerName, "headerName cannot be null");this.headerName = headerName;}/** * Sets the name of the HTTP parameter that is used to specify the session alias. If * the value is null, then only a single session is supported per browser. * * @param sessionAliasParamName the name of the HTTP parameter used to specify the * session alias. If null, then ony a single session is supported per browser. */public void setSessionAliasParamName(String sessionAliasParamName) {this.sessionParam = sessionAliasParamName;}/** * Sets the {@link CookieSerializer} to be used. * * @param cookieSerializer the cookieSerializer to set. Cannot be null. */public void setCookieSerializer(CookieSerializer cookieSerializer) {Assert.notNull(cookieSerializer, "cookieSerializer cannot be null");this.cookieSerializer = cookieSerializer;}/** * Sets the name of the cookie to be used. * @param cookieName the name of the cookie to be used * @deprecated use {@link #setCookieSerializer(CookieSerializer)} */@Deprecatedpublic void setCookieName(String cookieName) {DefaultCookieSerializer serializer = new DefaultCookieSerializer();serializer.setCookieName(cookieName);this.cookieSerializer = serializer;}/** * Sets the delimiter between a session alias and a session id when deserializing a * cookie. The default is " " This is useful when using * <a href="https://tools.ietf.org/html/rfc6265">RFC 6265</a> for writing the cookies * which doesn't allow for spaces in the cookie values. * * @param delimiter the delimiter to set (i.e. "_ " will try a delimeter of either "_" * or " ") */public void setDeserializationDelimiter(String delimiter) {this.deserializationDelimiter = delimiter;}/** * Sets the delimiter between a session alias and a session id when deserializing a * cookie. The default is " ". This is useful when using * <a href="https://tools.ietf.org/html/rfc6265">RFC 6265</a> for writing the cookies * which doesn't allow for spaces in the cookie values. * * @param delimiter the delimiter to set (i.e. "_") */public void setSerializationDelimiter(String delimiter) {this.serializationDelimiter = delimiter;}public Map<String, String> getSessionIds(HttpServletRequest request) {List<String> cookieValues = this.cookieSerializer.readCookieValues(request);String sessionCookieValue = cookieValues.isEmpty() ? "": cookieValues.iterator().next();Map<String, String> result = new LinkedHashMap<String, String>();StringTokenizer tokens = new StringTokenizer(sessionCookieValue,this.deserializationDelimiter);if (tokens.countTokens() == 1) {result.put(DEFAULT_ALIAS, tokens.nextToken());return result;}while (tokens.hasMoreTokens()) {String alias = tokens.nextToken();if (!tokens.hasMoreTokens()) {break;}String id = tokens.nextToken();result.put(alias, id);}return result;}public HttpServletRequest wrapRequest(HttpServletRequest request,HttpServletResponse response) {request.setAttribute(HttpSessionManager.class.getName(), this);return request;}public HttpServletResponse wrapResponse(HttpServletRequest request,HttpServletResponse response) {return new MultiSessionHttpServletResponse(response, request);}public String encodeURL(String url, String sessionAlias) {String encodedSessionAlias = urlEncode(sessionAlias);int queryStart = url.indexOf("?");boolean isDefaultAlias = DEFAULT_ALIAS.equals(encodedSessionAlias);if (queryStart < 0) {return isDefaultAlias ? url: url + "?" + this.sessionParam + "=" + encodedSessionAlias;}String path = url.substring(0, queryStart);String query = url.substring(queryStart + 1, url.length());String replacement = isDefaultAlias ? "" : "$1" + encodedSessionAlias;query = query.replaceFirst("((^|&)" + this.sessionParam + "=)([^&]+)?",replacement);String sessionParamReplacement = String.format("%s=%s", this.sessionParam,encodedSessionAlias);if (!isDefaultAlias && !query.contains(sessionParamReplacement)&& url.endsWith(query)) {// no existing aliasif (!(query.endsWith("&") || query.length() == 0)) {query += "&";}query += sessionParamReplacement;}return path + "?" + query;}private String urlEncode(String value) {try {return URLEncoder.encode(value, "UTF-8");}catch (UnsupportedEncodingException e) {throw new RuntimeException(e);}}/** * A {@link CookieHttpSessionStrategy} aware {@link HttpServletResponseWrapper}. */class MultiSessionHttpServletResponse extends HttpServletResponseWrapper {private final HttpServletRequest request;MultiSessionHttpServletResponse(HttpServletResponse response,HttpServletRequest request) {super(response);this.request = request;}private String getCurrentSessionAliasFromUrl(String url) {String currentSessionAliasFromUrl = null;int queryStart = url.indexOf("?");if (queryStart >= 0) {String query = url.substring(queryStart + 1);Matcher matcher = Pattern.compile(String.format("%s=([^&]+)",MyCookieHttpSessionStrategy.this.sessionParam)).matcher(query);if (matcher.find()) {currentSessionAliasFromUrl = matcher.group(1);}}return currentSessionAliasFromUrl;}@Overridepublic String encodeRedirectURL(String url) {String encodedUrl = super.encodeRedirectURL(url);String currentSessionAliasFromUrl = getCurrentSessionAliasFromUrl(encodedUrl);String alias = (currentSessionAliasFromUrl != null)? currentSessionAliasFromUrl : getCurrentSessionAlias(this.request);return MyCookieHttpSessionStrategy.this.encodeURL(encodedUrl, alias);}@Overridepublic String encodeURL(String url) {String encodedUrl = super.encodeURL(url);String currentSessionAliasFromUrl = getCurrentSessionAliasFromUrl(encodedUrl);String alias = (currentSessionAliasFromUrl != null)? currentSessionAliasFromUrl : getCurrentSessionAlias(this.request);return MyCookieHttpSessionStrategy.this.encodeURL(encodedUrl, alias);}}}
3.最终还是把问题解决,这个是某智教育公司的nigix服务器,人家就是不转发cookie,人后把我们项目所有人都坑了,还以为是我们自己代码有问题,第一次写博客,大神勿喷,多多指教。
4.在这位仁兄的博客上也学习到了很多    http://blog.csdn.net/qq351790934/article/details/54930049



阅读全文
1 0