preface

You have to be bald to be strong.

The text has been included in my GitHub repository. Welcome to Star: github.com/ZhongFuChen…

I was already looking at what single sign-on was before MY internship, but I was busy with other things during the internship, so a few sites have been lying in my favorites:

I’ve had a reader come to me a while back who implemented single sign-on using JWT (but the article doesn’t explain what single sign-on is), so I thought it was time to clean up.

  • Simple code to implement JWT(JSON Web Token) to complete SSO SSO

What is single sign-on?

The English name of Single Sign On is Single Sign On (SSO).

In the beginning/before, we usually have only one system, all the functions on the same system.

Later, we split the single system into subsystems in order to use resources properly and reduce coupling.

  • Review: Distributed basics

For example, Alibaba’s Taobao and Tmall, obviously we can know that these are two systems, but when you use, logged in tmall, Taobao will automatically logged in.

In simple terms, single sign-on is when a user logs in to multiple systems once and each system is aware that the user has logged in.

Review single system logins

When I first learn JavaWeb, login and registration is a function I do the most (beginner Servlet do, learn SpringMVC do, follow the project do…) I can’t tell you how many times I’ve done the login and registration function… Here is a brief description of how we first learned how to do the login function.

As we all know, HTTP is a stateless protocol, which means that the server cannot confirm the user’s information. So the W3C came up with the idea of giving every user a pass, and whoever accesses it needs to carry the pass, so that the server can verify the user’s information from the pass. A pass is a Cookie.

If cookies verify the user’s identity by checking the “pass” on the user, then sessions verify the user’s identity by checking the “client list” on the server. A Session creates a “client list” in the server.

The HTTP protocol is stateless, and a Session cannot determine whether it is the same user based on the HTTP connection. The server sends a Cookie named JESSIONID to the user’s browser, whose value is the Session ID value. So a Session actually uses a Cookie to identify the same user.

So, in general, we implement single system logins like this:

  • The login: Saves user information in the Session object
    • If you can find it in the Session object, you are logged in
    • If you can’t find it in the Session object, you are not logged in (or logged out).
  • Logout: Deletes a user’s information from a Session
  • Remember me (after closing the browser, I can open it again and stay logged in) : Use it with cookies

My previous Demo code, you can refer to:

 /** * User login */
@PostMapping(value = "/user/session", produces = {"application/json; charset=UTF-8"})
public Result login(String mobileNo, String password, String inputCaptcha, HttpSession session, HttpServletResponse response) {

    // Check whether the verification code is correct
    if (WebUtils.validateCaptcha(inputCaptcha, "captcha", session)) {

        // Determine if there is a user
        User user = userService.userLogin(mobileNo, password);
        if(user ! =null) {
            /* Set automatic login for one week. Save the token in the database */
            String loginToken = WebUtils.md5(new Date().toString() + session.getId());
            user.setLoginToken(loginToken);
            User user1 = userService.userUpload(user);

            session.setAttribute("user", user1);

            CookieUtil.addCookie(response,"loginToken",loginToken,604800);

            return ResultUtil.success(user1);

        } else {
            returnResultUtil.error(ResultEnum.LOGIN_ERROR); }}else {
        returnResultUtil.error(ResultEnum.CAPTCHA_ERROR); }}/** * User exits */
@DeleteMapping(value = "/session", produces = {"application/json; charset=UTF-8"})
public Result logout(HttpSession session,HttpServletRequest request,HttpServletResponse response ) {

    // Delete session and cookie
    session.removeAttribute("user");

    CookieUtil.clearCookie(request, response, "loginToken");

    return ResultUtil.success();
}
/ * * *@author ozc
* @version1.0 * <p> * interceptor; Realize automatic login function */
public class UserInterceptor implements HandlerInterceptor {


@Autowired
private UserService userService;

public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object o) throws Exception {
    User sessionUser = (User) request.getSession().getAttribute("user");

    // We have logged in
    if(sessionUser ! =null) {
        return true;
    } else {
        // Check whether the cookie brought to you exists
        String loginToken = CookieUtil.findCookieByName(request, "loginToken");
        if (StringUtils.isNotBlank(loginToken)) {
            // Check the database for the Cookie
            User user = userService.findUserByLoginToken(loginToken);
            if(user ! =null) {
                request.getSession().setAttribute("user", user);
                return true;
            } else {
                // There is no user for this Cookie (Cookie does not match)
                CookieUtil.clearCookie(request, response, "loginToken");
                return false; }}else {

            // No cookie, no login. Yes index Requests user information and permits permission
            if (request.getRequestURI().contains("session")) {
                return true;
            }

            // No cookie credentials
            response.sendRedirect("/login.html");
            return false; }}}}Copy the code

To summarize the idea of the above code:

  • When a user logs in, authenticate the user’s account and password
  • Generate a Token to save in the database, write the Token to the Cookie
  • Store user data in the Session
  • Cookie will be brought when the request, check whether there is a login, if the login is allowed

If you don’t understand this, I suggest you review sessions and cookies and HTTP:

  • This section describes the session technology, Cookie API, and its application
  • Session introduction, API, lifecycle, application, and Cookie
  • What is the HTTP

Three, multi – system login problems and solutions

3.1 Session Sharing Failure

The single system login function is mainly implemented by using Session to save user information. However, it is clear that multiple systems may have multiple Tomcat, and Session depends on the Tomcat of the current system, so the Session of system A and system B are not shared.

There are several solutions to solve the problem of not sharing sessions between systems:

  • Tomcat cluster Session global replication (each Tomcat Session in a cluster is completely synchronized)
  • Hash the requested IP addresses to the corresponding machines (this means that the requested IP addresses will always access the same server)
  • Session data in Redis (using Redis to simulate sessions)advice
    • If you don’t know Redis, please move on.

We can isolate the login function as a subsystem.

SSO (Login system) logic is as follows:

// Login function (a separate service for SSO)
@Override
public TaotaoResult login(String username, String password) throws Exception {
	
	// Query user information by user name
	TbUserExample example = new TbUserExample();
	Criteria criteria = example.createCriteria();
	criteria.andUsernameEqualTo(username);
	List<TbUser> list = userMapper.selectByExample(example);
	if (null == list || list.isEmpty()) {
		return TaotaoResult.build(400."User does not exist");
	}
	// Check the password
	TbUser user = list.get(0);
	if(! DigestUtils.md5DigestAsHex(password.getBytes()).equals(user.getPassword())) {return TaotaoResult.build(400."Password error");
	}
	// Write user information to redis
	// Generate a user token
	String token = UUID.randomUUID().toString();
	jedisCluster.set(USER_TOKEN_KEY + ":" + token, JsonUtils.objectToJson(user));
	// Set the session expiration time
	jedisCluster.expire(USER_TOKEN_KEY + ":" + token, SESSION_EXPIRE_TIME);
	return TaotaoResult.ok(token);
}

Copy the code

When other subsystems log in, they request SSO (login system) to log in, write the returned token into a Cookie, and bring the Cookie with them the next time they access:

public TaotaoResult login(String username, String password, HttpServletRequest request, HttpServletResponse response) {
	// Request parameters
	Map<String, String> param = new HashMap<>();
	param.put("username", username);
	param.put("password", password);
	// Login processing
	String stringResult = HttpClientUtil.doPost(REGISTER_USER_URL + USER_LOGIN_URL, param);
	TaotaoResult result = TaotaoResult.format(stringResult);
	// Login error
	if(result.getStatus() ! =200) {
		return result;
	}
	// After a successful login, obtain the token information and write the cookie
	String token = (String) result.getData();
	/ / write cookies
	CookieUtils.setCookie(request, response, "TT_TOKEN", token);
	// Return success
	return result;
	
}
Copy the code

Conclusion:

  • The SSO system generates a token, saves the user information in Redis, and sets the expiration time
  • Other systems request SSO system for login, obtain the token returned by SSO, and write it to Cookie
  • Each time the request is made, the Cookie will be carried, and the interceptor will get the token to determine whether the login has been made

At this point, there are actually two changes:

  • Extract the login function to a single system (SSO), and other systems request SSO for login
  • We used to save user information in Session, now we save user information in Redis

3.2 Cookie cross-domain problem

We solved the problem of sessions not being shared, but there is another problem. Cookies are not cross-domain

For example, when we request
, the browser automatically sends the Google.com Cookie to Google’s servers, You don’t bring cookies from
to Google’s servers.

This means that, due to the different domain names, after the user logs in to system A, system A returns the Cookie to the browser. When the user requests system B again, the Cookie of system A will not be brought to the browser.

There are several solutions to the Cookie cross-domain problem:

  1. After the server writes the Cookie to the client, the client parses the Cookie and resolves the Token. After that, all requests take the Token with them
  2. Multiple domains share cookies and set the domain of the Cookie when written to the client.
  3. Store tokens in the SessionStroage (no cross-domain issues without relying on cookies)

At this point, we are ready to implement single sign-on.

3.3 the CAS theory

When it comes to single sign-on (SSO), there’s a term you’ll see: Central Authentication Service (CAS). Here’s how it works.

If we’ve extracted the login into a separate system, we can still play like this. Now we have two systems, www.java3y.com and www.java4y.com, and one SSOwww.sso.com

First, the user wants to access A limited resource (such as the shopping cart feature, which can only be accessed after login) from system Awww.java3y.com. System Awww.java3y.com finds that the user is not logged in and redirects to the SSO authentication center with his address as the parameter. The requested address is as follows:

  • www.sso.com?service=www.java3y.com

The SSO authentication center detects that the user has not logged in and directs the user to the login page. The user enters the user name and password to log in. The user establishes a global session with the SSO authentication Center (generates a Token, writes it to a Cookie, and saves it on the browser).

The certification authority then redirects back to System A and carries the Token to the following address:

  • www.java3y.com?token=xxxxxxx

System A then goes to the SSO authentication center to verify that the Token is correct. If it is correct, system A establishes A local Session with the user. At this point, System A and the user are logged in.

At this point, the user wants to access limited resources of system Bwww.java4y.com (such as the order function, which can only be accessed after login). System Bwww.java4y.com finds that the user is not logged in, so it redirects to the SSO authentication center with its own address as the parameter. The requested address is as follows:

  • www.sso.com?service=www.java4y.com

Note that since the user has previously established a global session with the authentication authority www.sso.com (the cookies have been saved to the browser), this time system B is redirected to authentication authority www.sso.com with cookies.

Based on the Cookie that the authentication authority has established a global session with the user, the authentication authority redirects back to system B and carries the Token to system B. The redirection address is as follows:

  • www.java4y.com?token=xxxxxxx

System B then goes to the SSO authentication center to verify that the Token is correct. If it is correct, system B establishes a local Session with the user. At this point, system B and the user are logged in.

At this point, the SSO Certification Center is like a relay station.

References:

The last

Happy to export dry goods of Java technology public number: Java3y. There are more than 200 original technical articles, massive video resources, beautiful brain maps, attention can be obtained!

Think my article is good, thumbs up!