優勢:配置簡單
缺點:若是機器多了,就會出現大量的網絡傳輸,甚至容易引發網絡風暴,致使系統崩潰,只能適合少數的機器。前端
下面是一個springboot+springSession+redis共享的列子java
<?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 @EnableRedisHttpSession public class Config { /** *jedis簡單配置 * @return */ @Bean public JedisConnectionFactory connectionFactory() { JedisConnectionFactory connectionFactory = new JedisConnectionFactory(); connectionFactory.setPort(6379); connectionFactory.setHostName("192.168.31.100"); return connectionFactory; } /** *CookieHttpSessionStrategy 配置 * @return */ @Bean public 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 @ComponentScan public 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; /** *測試類 */ @RestController public 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放到服務器上進行測試web
配置我本地的host 配置文件redis
192.168.31.100 top.sumei.com
192.168.31.101 bottom.sumei.com算法
用 java -jar login-0.0.1-SNAPSHOT.jar啓動服務 瀏覽器地址spring
發現 top.sumei.com 與 bottom.sumei.com 的sesionid 是同樣的,說明session共享成功。數據庫
jws 相比Cookie的優點apache
支持跨域跨站點訪問:json
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; @RestController public 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的端口。
在瀏覽器輸入 http://localhost:8881/testToken?token=eyJhbGciOiJIUzI1NiJ9.eyJ1aWQiOnsidWlkIjoiMDAxIiwiZXhwcmllIjowfSwiZXhwIjoxNTIxMzY2OTkxfQ.FSfsjldW7jr8TNHlfREu6l_nDPQQ33LdvMydem3ZSas
在8881中我並無用瀏覽器傳入uid=1 說明經過這種方式服務之間能夠進行共享數據,能夠解決單點登陸。