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


声明:本文转载自https://my.oschina.net/u/2474435/blog/1645027,转载目的在于传递更多信息,仅供学习交流之用。如有侵权行为,请联系我,我会及时删除。

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 @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放到服务器上进行测试

配置我本地的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;  @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 说明通过这种方式服务之间可以进行共享数据,可以解决单点登录。

本文发表于2018年03月18日 22:38
(c)注:本文转载自https://my.oschina.net/u/2474435/blog/1645027,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责。如有侵权行为,请联系我们,我们会及时删除.

阅读 1889 讨论 0 喜欢 1

抢先体验

扫码体验
趣味小程序
文字表情生成器

闪念胶囊

你要过得好哇,这样我才能恨你啊,你要是过得不好,我都不知道该恨你还是拥抱你啊。

直抵黄龙府,与诸君痛饮尔。

那时陪伴我的人啊,你们如今在何方。

不出意外的话,我们再也不会见了,祝你前程似锦。

这世界真好,吃野东西也要留出这条命来看看

快捷链接
网站地图
提交友链
Copyright © 2016 - 2021 Cion.
All Rights Reserved.
京ICP备2021004668号-1