1.两个服务器通过同步session实现session共享

优点:配置简单
缺点:如果机器多了,就会出现大量的网络传输,甚至容易引起网络风暴,导致系统崩溃,只能适合少数的机器。
2.将session存储在某个介质上、比如数据库上或者缓存服务器上,进行统一管理。

下面是一个springboot+springSession+redis共享的列子
<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.sumei</groupId><artifactId>login</artifactId><version>0.0.1-SNAPSHOT</version><packaging>jar</packaging><name>login</name><description>Demo project for Spring Boot</description><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>1.5.10.RELEASE</version><relativePath/> <!-- lookup parent from repository --></parent><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding><java.version>1.8</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><dependency><groupId>org.springframework.session</groupId><artifactId>spring-session-data-redis</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.session</groupId><artifactId>spring-session</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build><repositories><repository><id>spring-milestone</id><url>https://repo.spring.io/libs-release</url></repository></repositories></project>
package com.sumei.login;import org.springframework.session.web.http.DefaultCookieSerializer;import javax.servlet.http.HttpServletRequest;/*** 自定义CookiePath*/public class CustomerCookiesSerializer extends DefaultCookieSerializer {private String getCookiePath(HttpServletRequest request) {return "/";}}
package com.sumei.login;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;import org.springframework.session.web.http.CookieHttpSessionStrategy;@Configuration@EnableRedisHttpSessionpublic class Config {/***jedis简单配置* @return*/@Beanpublic JedisConnectionFactory connectionFactory() {JedisConnectionFactory connectionFactory = new JedisConnectionFactory();connectionFactory.setPort(6379);connectionFactory.setHostName("192.168.31.100");return connectionFactory;}/***CookieHttpSessionStrategy 配置* @return*/@Beanpublic CookieHttpSessionStrategy cookieHttpSessionStrategy(){CookieHttpSessionStrategy cookieHttpSessionStrategy=new CookieHttpSessionStrategy();CustomerCookiesSerializer cookiesSerializer= new CustomerCookiesSerializer();cookiesSerializer.setDomainName("sumei.com");cookieHttpSessionStrategy.setCookieSerializer(cookiesSerializer);return cookieHttpSessionStrategy;}}
package com.sumei.login;import org.springframework.session.web.context.AbstractHttpSessionApplicationInitializer;public class Initializer extends AbstractHttpSessionApplicationInitializer {public Initializer() {super(Config.class);}}
package com.sumei.login;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.context.annotation.ComponentScan;@SpringBootApplication@ComponentScanpublic class LoginApplication {public static void main(String[] args) {SpringApplication.run(LoginApplication.class, args);}}
package com.sumei.login;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpSession;import java.util.HashMap;import java.util.Map;/***测试类*/@RestControllerpublic class LoginController {@RequestMapping("test1")public Map getSessionId(HttpServletRequest request){HttpSession session = request.getSession(false);String session_id=null;if(session==null){session=request.getSession(true);}session_id = session.getId();System.out.println(session_id);Map<String,Object> res=new HashMap<>();res.put("sessionid",session_id);if(session.getAttribute("boot")==null){System.out.println("***********************");session.setAttribute("boot","nbbo");}else {System.out.println(session.getAttribute("boot"));}return res;}}
将其打成jar放到服务器上进行测试
配置我本地的host 配置文件
192.168.31.100 top.sumei.com
192.168.31.101 bottom.sumei.com
用 java -jar login-0.0.1-SNAPSHOT.jar启动服务 浏览器地址


发现 top.sumei.com 与 bottom.sumei.com 的sesionid 是一样的,说明session共享成功。
3.基于JWT(JSON WEB TOKEN)代替的方案

jws 相比Cookie的优势
支持跨域跨站点访问:
Cookie是不允许垮域访问的,可以通过设置顶级域名的方式实现部分跨域,但是跨站点的访问仍然不支持,
如果使用Token机制,就可以通过HTTP头传输用户认证信息,从而更好的实现跨域跨站点。
无状态:
Token机制在服务端不需要存储session信息,Token自身包含了登录用户的信息,只需要在客户端的cookie或本地介质存储状态信息;
更适用于移动应用:
当客户端是原生应用时,Cookie是不被支持的,虽然目前Webview的方式可以解决Cookie问题,
但是显然采用Token认证机制会简单得多;
安全性更强:
因为不再依赖于Cookie,所以你就不需要考虑对CSRF(跨站请求伪造)的防范;
标准化易扩展:
可以采用标准化的 JSON Web Token (JWT),对以后系统接入Node等纯前端开发更便捷;
相比Session一致性提高性能:
相比服务端保存Session一致性信息,并查询用户登录状态,一般来说Token的验证过程(包含加密和解密),性能开销会更小。
JWS 由三部分组成(header,payload,Signature)
header {
“alg”: “HS256”, //加密算法
“typ”: “JWT” //令牌类型
}
payload{
{“iss”,value},//签发者
{“iat”, value},//非必须。issued at。 token创建时间,unix时间戳格式
{“exp”, value},//过期时间
{“aud”, value},//非必须。接收该JWT的一方。
{“sub”, value},//拥有者
{“jti”, value},//非必须。JWT ID。针对当前token的唯一标识
//自定义claims
{“user”,value}
{“passwd”,value}
……….
};
signature 就是用点号将header和payload联系起来,然后用header里面指定的加密方法进行加密后的字符串。
一个简单的测试demo
package com.sumei.login.jwt;public class JSONTokenInfo {/*** 过期时间*/private String uid;/*** 用户id*/private int exprie;public JSONTokenInfo() {}public JSONTokenInfo(String uid) {this.uid = uid;}public String getUid() {return uid;}public void setUid(String uid) {this.uid = uid;}public int getExprie() {return exprie;}public void setExprie(int exprie) {this.exprie = exprie;}}
package com.sumei.login.jwt;import io.jsonwebtoken.Claims;import io.jsonwebtoken.Jws;import io.jsonwebtoken.Jwts;import io.jsonwebtoken.SignatureAlgorithm;import org.joda.time.DateTime;import javax.crypto.spec.SecretKeySpec;import javax.xml.bind.DatatypeConverter;import java.security.Key;public class JWSTokenUtil {private static String jwt_key_user_id="uid";/*** 生成一个加密key* @return*/public static Key getKey(){SignatureAlgorithm es256 = SignatureAlgorithm.HS256;byte[] bytes= DatatypeConverter.parseBase64Binary("secret key");SecretKeySpec secretKeySpec = new SecretKeySpec(bytes,es256.getJcaName());return secretKeySpec;}/*** 生成token* @param jsonTokenInfo* @param exprie* @return*/public static String getToken(JSONTokenInfo jsonTokenInfo,int exprie){return Jwts.builder().claim(jwt_key_user_id,jsonTokenInfo).setExpiration(DateTime.now().plusSeconds(exprie).toDate()).signWith(SignatureAlgorithm.HS256,getKey()).compact();}/**** @param token* @return*/public static JSONTokenInfo getInstance(String token) {Jws<Claims> claimsJws = Jwts.parser().setSigningKey(getKey()).parseClaimsJws(token);Claims body = claimsJws.getBody();JSONTokenInfo jsonTokenInfo=new JSONTokenInfo();jsonTokenInfo.setUid(body.get(jwt_key_user_id).toString());return jsonTokenInfo;}}
package com.sumei.login;import com.sumei.login.jwt.JSONTokenInfo;import com.sumei.login.jwt.JWSTokenUtil;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import javax.servlet.http.HttpSession;import java.util.HashMap;import java.util.Map;@RestControllerpublic class JWSTokenController {/**** @param uid* @param response* @return*/@RequestMapping("testJws")public String testJws(String uid,HttpServletResponse response){JSONTokenInfo jsonTokenInfo=new JSONTokenInfo(uid);String token = JWSTokenUtil.getToken(jsonTokenInfo, 3000);response.addHeader("Set-Cookie","access_token="+token+"PATH=/;HttpOnly");System.out.println(token);return token;}/*** 根据token检查uuid* @param token* @return*/@RequestMapping("testToken")public JSONTokenInfo testToken(String token){JSONTokenInfo jsonTokenInfo = JWSTokenUtil.getInstance(token);return jsonTokenInfo;}}
打成jar 进行本地测试
启动java -jar *.jar 启动两个服务 一个端口为8888 一个端口为8881
1.浏览器输入 http://localhost:8888/testJws?uid=001

2.复制token 将这个token 传递给8881的端口。

在8881中我并没有用浏览器传入uid=1 说明通过这种方式服务之间可以进行共享数据,可以解决单点登录。
文章来源:https://my.oschina.net/u/2474435/blog/1645027
相关文章推荐:http://www.roncoo.com/article/index?title=session
spring boot 视频教程:http://www.roncoo.com/course/list.html?courseName=spring+boot
