Session共享方法以及單點登陸方案

1.兩個服務器經過同步session實現session共享


優勢:配置簡單
缺點:若是機器多了,就會出現大量的網絡傳輸,甚至容易引發網絡風暴,致使系統崩潰,只能適合少數的機器。前端

2.將session存儲在某個介質上、好比數據庫上或者緩存服務器上,進行統一管理。

       

    下面是一個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共享成功。數據庫

 

3.基於JWT(JSON WEB TOKEN)代替的方案

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 說明經過這種方式服務之間能夠進行共享數據,能夠解決單點登陸。

相關文章
相關標籤/搜索