Session共享方法以及单点登录方案

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


Session共享方法以及单点登录方案_第1张图片
image

优点:配置简单
缺点:如果机器多了,就会出现大量的网络传输,甚至容易引起网络风暴,导致系统崩溃,只能适合少数的机器。

2.将session存储在某个介质上、比如数据库上或者缓存服务器上,进行统一管理。


Session共享方法以及单点登录方案_第2张图片
image

下面是一个springboot+springSession+redis共享的列子



   4.0.0

   com.sumei
   login
   0.0.1-SNAPSHOT
   jar

   login
   Demo project for Spring Boot

   
      org.springframework.boot
      spring-boot-starter-parent
      1.5.10.RELEASE
       
   

   
      UTF-8
      UTF-8
      1.8
   

   
      
         org.springframework.boot
         spring-boot-starter-data-redis
      

      
         org.springframework.session
         spring-session-data-redis
      

      
         org.springframework.boot
         spring-boot-starter-web
      

      
         org.springframework.session
         spring-session

      

      
         org.springframework.boot
         spring-boot-starter-test
         test
      

   

   
      
         
            org.springframework.boot
            spring-boot-maven-plugin
         
      
   

   
      
         spring-milestone
         https://repo.spring.io/libs-release
      
   



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 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启动服务 浏览器地址


Session共享方法以及单点登录方案_第3张图片
image
Session共享方法以及单点登录方案_第4张图片
image

发现 top.sumei.com 与 bottom.sumei.com 的sesionid 是一样的,说明session共享成功。

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


Session共享方法以及单点登录方案_第5张图片
image

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 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

Session共享方法以及单点登录方案_第6张图片
image

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

在浏览器输入 http://localhost:8881/testToken?token=eyJhbGciOiJIUzI1NiJ9.eyJ1aWQiOnsidWlkIjoiMDAxIiwiZXhwcmllIjowfSwiZXhwIjoxNTIxMzY2OTkxfQ.FSfsjldW7jr8TNHlfREu6l_nDPQQ33LdvMydem3ZSas

Session共享方法以及单点登录方案_第7张图片
image

在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

你可能感兴趣的:(Session共享方法以及单点登录方案)