Session sharing method and single sign-on scheme

1. Two servers realize session sharing by synchronizing sessions


Advantages: Simple configuration
Disadvantage: If there are too many machines, there will be a large number of network transmissions, and it is easy to cause network storms, resulting in system crashes, which can only be suitable for a small number of machines.

2. Store the session on a medium, such as a database or a cache server, for unified management.

       

    The following is a list shared by 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;

/**
 * Custom 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 simple configuration
     * @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;

/**
 *Test class
 */
@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;
    }
}

Put it into a jar and put it on the server for testing

Configure my local host configuration file

192.168.31.100 top.sumei.com
192.168.31.101 bottom.sumei.com

Start the service browser address with java -jar login-0.0.1-SNAPSHOT.jar

 

It is found that the sessionid of top.sumei.com and bottom.sumei.com are the same, indicating that session sharing is successful.

 

3. Alternatives based on JWT (JSON WEB TOKEN)

Advantages of jws over cookies

Support cross-domain and cross-site access:

Cookies are not allowed to access from broken domains. Some cross-domains can be achieved by setting top-level domain names, but cross-site access is still not supported.
If the Token mechanism is used, user authentication information can be transmitted through HTTP headers, so as to better achieve Cross-domain cross-site.

no status:

The Token mechanism does not need to store session information on the server side. The Token itself contains the information of the logged-in user, and only needs to store the status information in the client's cookie or local medium;

More suitable for mobile applications:

When the client is a native application, cookies are not supported, although the current Webview method can solve the cookie problem,

However, it is obviously much simpler to use the Token authentication mechanism;

Stronger security:

Because you no longer rely on cookies, you don't need to consider CSRF (cross-site request forgery) prevention;

Standardized and easy to expand:

Standardized JSON Web Token (JWT) can be used, which is more convenient for pure front-end development such as system access to Node in the future;

Improved performance compared to Session consistency:

Compared with the server saving session consistency information and querying the user's login status, generally speaking, the token verification process (including encryption and decryption) has lower performance overhead.

JWS consists of three parts (header, payload, Signature)

header {

    "alg": "HS256", //encryption algorithm 

    "typ": "JWT" ​​//Token type

}

payload{
                    {"iss",value},//issuer
                    {"iat", value},//not required. issued at. Token creation time, unix timestamp format
                    {"exp", value},//Expiration time
                    {"aud", value},//Not required. The party that received this JWT.
                    {"sub", value},//Owner
                    {"jti", value},//Not required. JWT ID. Unique identifier for the current token
                      
            //custom claims

            {"user",value}
            {"passwd",value}
            ..........
                };


    The signature is a string that connects the header and the payload with a dot, and then encrypts it with the encryption method specified in the header.

A simple test demo

    

package com.sumei.login.jwt;

public class JSONTokenInfo {
    /**
     * Expiration
     */
    private  String uid;
    /**
     * userid
     */
    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";
    /**
     * Generate an encryption 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;
    }

    /**
     * Generate 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;
        }

    /**
     * Check uuid against token
     * @param token
     * @return
     */
    @RequestMapping("testToken")
        public JSONTokenInfo testToken(String token){
            JSONTokenInfo jsonTokenInfo = JWSTokenUtil.getInstance(token);
            return  jsonTokenInfo;
        }
}

 

Make a jar for local testing

Start java -jar ***.jar to start two services one port is 8888 and one port is 8881

 1. Browser input http://localhost:8888/testJws?uid=001

    2. Copy the token and pass this token to the port of 8881.

Enter http://localhost:8881/testToken?token=eyJhbGciOiJIUzI1NiJ9.eyJ1aWQiOnsidWlkIjoiMDAxIiwiZXhwcmllIjowfSwiZXhwIjoxNTIxMzY2OTkxfQ.FSfsjldW7jr8TNHlfREu6l_nDPQQ33LdvMydem3ZSas


In 8881, I did not use the browser to pass in uid=1, indicating that data can be shared between services in this way, and single sign-on can be solved.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325546007&siteId=291194637