SSO单点登录

走远了吗. 提交于 2020-04-06 07:35:15

前言:今天刚学完B站up主“楠哥教你学Java”前些日子的一个直播教学,通过录播跟着把代码敲了一遍,整理了一下。前半部分取自楠哥的笔记,后部分代码和思维导图自己归纳总结,代码细节今天是撸不完了,明后天在多研究几遍一些细节

单点登录

什么是单点登录:一处登录,处处登录,一处登出,处处登出。

用户只需要登录一次就可以访问所有相互信任的应用系统。

SSO

Single Sign On 单点登录

企业业务整合解决方案

一票通

SSO 原理

  • 当用户第一次访问淘宝的时候,因为还没有登录,会被引导到认证中心进行登录。
  • 根据用户提供的登录信息,认证系统进行身份验证,如果通过,则登录成功,并返回给用户一个认证的凭据(token)。
  • 当用户访问天猫时,就会将这个 token 带上,作为自己认证的凭据。
  • 应用系统接收到请求后会把 token 送到认证中心进行校验,检查 token 的合法性。
  • 如果通过校验,用户就可以在不用再次登录的情况下访问天猫了。

SSO 实现技术

Cookie 单点登录

使用 Cookie 作为媒介,存放用户凭证。

用户登录淘宝之后,返回一个 token,存入客户端的 Cookie 中,当用户访问天猫的时候,会自动带上 Cookie,这样 token 又传给了认证中心,进行校验。

分布式 Session

1、用户第一次登录时,将会话信息,写入分布式 Session。

2、用户再次登录时,获取分布式 Session,判断是否有登录信息,如果没有则返回登录页面。

3、Session 一般存储到 Redis 中,因为它有持久化功能,如果分布式 Session 宕机后,重启之后可以从持久化存储中重新加载会话信息。

SSO 常见方案

OAuth2(第三方登录授权)

第三方系统访问主系统资源,用户无需将主系统的账户告知第三方,只需要通过主系统的授权,第三方就可以使用主系统的资源。

jwt

Json Web Token,是为了在网络应用之间传递信息而执行的一种,基于 JSON 的开放标准,难度较高,需要了解很多协议,偏向底层的东西,需要你基于 JWT 认证协议,自己开放 SSO 服务和权限控制。

CAS(不是并发的 CAS)

单点登录的 CAS 和并发的 CAS 完全是两码事

中央认证服务,Central Authentication Service

CAS 是耶鲁大学发起的一个开源项目,为 Web 应用系统提供单点登录的解决方案,实现多个系统只需要登录一次,无需重复登录。

  • CAS Server
  • CAS Client

Server 和 Client 分别独立部署,Server 主要负责认证工作

Client 负责处理对客户端资源的访问请求,需要登录时,重定向到 Server 进行认证。

1、授权服务器保存一个全局 session,多个客户端各自保存自己的 session。

2、客户端登录时判断自己的 session 是否已经登录,如果没有登录,则重定向到服务器进行授权(带上自己的地址,用于回调)。

3、授权服务器判断全局的 session 是否已经登录,如果未登录则重定向到登录页面,提供用户登录,登录成功之后,授权服务器重定向到客户端,带上 ticket。

4、客户端收到 ticket 后,请求服务器获取用户信息。

5、服务器同意客户端授权后,服务器保存用户信息到全局 session,客户端将用户信息保存至本地 session。

手写单点登录系统架构

Spring Boot + Thymelaf

Thymeleaf 是个什么?

简单说, Thymeleaf 是一个跟 Velocity、FreeMarker 类似的模板引擎,它可以完全替代 JSP 。相较与其他的模板引擎,它有如下三个极吸引人的特点:

1.Thymeleaf 在有网络和无网络的环境下皆可运行,即它可以让美工在浏览器查看页面的静态效果,也可以让程序员在服务器查看带数据的动态页面效果。这是由于它支持 html 原型,然后在 html 标签里增加额外的属性来达到模板+数据的展示方式。浏览器解释 html 时会忽略未定义的标签属性,所以 thymeleaf 的模板可以静态地运行;当有数据返回到页面时,Thymeleaf 标签会动态地替换掉静态内容,使页面动态显示。

2.Thymeleaf 开箱即用的特性。它提供标准和spring标准两种方言,可以直接套用模板实现JSTL、 OGNL表达式效果,避免每天套模板、该jstl、改标签的困扰。同时开发人员也可以扩展和创建自定义的方言。

3.Thymeleaf 提供spring标准方言和一个与 SpringMVC 完美集成的可选模块,可以快速的实现表单绑定、属性编辑器、国际化等功能。

SSO(主工程) 创建三个子工程

  • taobao (客户端)
  • tmall (客户端)
  • server (服务端)

SSO_Client_Taobao

1、首先配置淘宝客户端的配置文件application.yml(访问端口、thymeleaf 模板、前后缀、标头、编码)

server:
  port: 8081
spring:
  thymeleaf:
    suffix: .html
    prefix: classpath:/templates/
    servlet:
      content-type: text/html
    encoding: UTF-8

2、resources下创建templates目录,写入index.html静态页面

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" th:href="@{/layui/css/layui.css}" media="all">
</head>
<body>
<div class="layui-container" style="width: 700px;height: 600px;margin-top: 0px;padding-top: 60px;">

    <h1>淘宝首页</h1>

    <div style="margin-left: 460px; width: 200px;">
        欢迎回来!admin
        <a th:href="${serverLogoutUrl}">
            <button class="layui-btn layui-btn-warm layui-btn-radius">退出</button>
        </a>
    </div>

    <img width="700px" th:src="@{/images/taobao.png}">

</div>
</body>
</html>

3、static目录导入layui,layui是一个前端框架

​ static目录导入一个图片模拟淘宝首页

4、创建运行类TaobaoApplication

package com.janeroad;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class TaobaoApplication {
    public static void main(String[] args) {
        SpringApplication.run(TaobaoApplication.class,args);
    }
}

5、创建控制器 TaobaoHandler,处理异步请求

package com.janeroad.controller;

import com.janeroad.util.SSOClientUtil;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.servlet.http.HttpSession;

[@Controller](https://my.oschina.net/u/1774615)
public class TaobaoHandler {
    @GetMapping("/taobao")
    public String index(Model model){
       model.addAttribute("serverLogoutUrl", SSOClientUtil.getServerLogoutUrl());
        return "index";
    }
    @RequestMapping("/logout")
    public String logout(HttpSession session){
        session.invalidate();
        return "redirect:/taobao";
    }
}

6、创建拦截器TaobaoInterceptor,继承与HandelerInterceptor,用于当请求服务端页面时判断用户是否登录,如果登录过放行,如果未登录跳转登录页面

package com.janeroad.interceptor;

import com.janeroad.util.HttpUtil;
import com.janeroad.util.SSOClientUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.servlet.HandlerInterceptor;
import org.thymeleaf.util.StringUtils;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.HashMap;

@Slf4j
public class TaobaoInterceptor implements HandlerInterceptor {
    
    /**
     * 功能描述: <br>
     * true 放行
     * false 不放行
     * @Param: [request, response, handler]
     * @Return: boolean
     * @Author: JaneRoad
     * @Date: 2020/3/29 15:30
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //1、判断是否已经登陆
        HttpSession session =request.getSession();
        Boolean isLogin =(Boolean) session.getAttribute("isLogin");
        if(isLogin!=null && isLogin){
            return true;
        }
        //2、判断 token
        String token=request.getParameter("token");
        if(!StringUtils.isEmpty(token)){
            //验证token
            log.info("token存在,需要验证");
            //发起验证
            String httpUrl = SSOClientUtil.SERVER_HOST_URL+"/verify";
            HashMap<String,String> params = new HashMap<>();
            params.put("token",token);
            params.put("clientLogoutUrl",SSOClientUtil.getClientLogoutUrl());
            String isVerify = HttpUtil.sendHttpRequest(httpUrl,params);
            if("true".equals(isVerify)){
                log.info("token验证通过,token={}",token);
                //token保存到本地Cookie
                Cookie cookie = new Cookie("token",token);
                response.addCookie(cookie);
                session.setAttribute("isLogin",true);
                return true;
            }
        }

        //3、跳转到认证中心进行登录
        SSOClientUtil.redirectToCheckToken(request, response);
        return false;
    }
}

7、创建工具类SSOClientUtil

package com.janeroad.util;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Properties;

public class SSOClientUtil {

    private static Properties properties = new Properties();
    public static String SERVER_HOST_URL;
    public static String CLIENT_HOST_URL;

    static {
        try {
            properties.load(SSOClientUtil.class.getClassLoader().getResourceAsStream("sso.properties"));
            SERVER_HOST_URL = properties.getProperty("server.host.url");
            CLIENT_HOST_URL = properties.getProperty("client.host.url");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //跳转到认证中心
    public static void redirectToCheckToken(HttpServletRequest request, HttpServletResponse response){
        StringBuffer url=new StringBuffer();
        url.append(SERVER_HOST_URL)
                .append("/checkToken?redirectUrl=")
                .append(CLIENT_HOST_URL)
                .append(request.getServletPath());
        try {
            response.sendRedirect(url.toString());
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    public static String getServerLogoutUrl(){
        return SERVER_HOST_URL+"/logout";
    }

    public static String getClientLogoutUrl(){
        return CLIENT_HOST_URL+"/logout";
    }
}

8、resources下写入一个配置文件sso.properties,用于记录服务端地址

server.host.url=http://localhost:8080
client.host.url=http://localhost:8081

9、创建工具类HttpUtil

package com.janeroad.util;

import org.springframework.util.StreamUtils;

import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.Map;

public class HttpUtil {
    /**
     * id=1
     * name=tom
     * {id=1,name=tom} id=1&name=tom
     * @param httpUrl
     * @param params
     * @return
     */
    public static String sendHttpRequest(String httpUrl, Map<String,String> params){
        try {
            URL url = new URL(httpUrl);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.setRequestMethod("POST");
            connection.setDoOutput(true);
            if(params!=null && params.size()>0){
                StringBuffer stringBuffer = new StringBuffer();
                for(Map.Entry<String,String> entry:params.entrySet()){
                    stringBuffer.append("&")
                            .append(entry.getKey())
                            .append("=")
                            .append(entry.getValue());
                }
                connection.getOutputStream().write(stringBuffer.substring(1).toString().getBytes("UTF-8"));
            }
            connection.connect();
            String response = StreamUtils.copyToString(connection.getInputStream(), Charset.forName("UTF-8"));
            return response;
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e){
            e.printStackTrace();
        }
        return null;
    }
}

SSO_Server

1、首先配置服务端的配置文件application.yml(访问端口、thymeleaf 模板、前后缀、标头、编码)

server:
  port: 8080
spring:
  thymeleaf:
    prefix: classpath:/templates/
    suffix: .html
    servlet:
      content-type: text/html
    encoding: UTF-8

2、resources下创建templates目录,写入index.html静态页面

<!DOCTYPE html>
<html lang="en">
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="/layui/css/layui.css"  media="all">
</head>
<body>
<div class="layui-container" style="width: 500px;height: 330px;margin-top: 130px;border: 1px solid #009688;padding-top: 60px;border-radius: 15px">
    <form class="layui-form" action="/login" method="post">
        <input type="hidden" name="redirectUrl" th:value="${redirectUrl}">
        <div class="layui-form-item">
            <label class="layui-form-label">用户名</label>
            <div class="layui-inline">
                <input type="text" name="username" lay-verify="username" autocomplete="off" placeholder="请输入用户名" class="layui-input">
            </div>
        </div>
        <div class="layui-form-item">
            <label class="layui-form-label">密码</label>
            <div class="layui-inline">
                <input type="password" name="password" lay-verify="password" placeholder="请输入密码" autocomplete="off" class="layui-input">
            </div>
        </div>
        <div class="layui-form-item">
            <button class="layui-btn" lay-submit="" lay-filter="demo2" style="margin-left: 160px;">登陆</button>
        </div>
    </form>
</div>
<script src="/layui/layui.js" charset="utf-8"></script>
<script>
    layui.use(['form'], function(){
        var form = layui.form;

        //自定义验证规则
        form.verify({
            username: function(value){
                if(value.length == 0){
                    return '用户名不能为空';
                }
            }
            ,password: [/(.+){6,12}$/, '密码必须6到12位']
        });

    });
</script>
</body>
</html>

3、同客户端,static目录导入layui,layui是一个前端框架

4、创建运行类ServerApplication

package com.janeroad;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class ServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(ServerApplication.class,args);
    }
}

5、创建控制器 ServerHandler,处理异步请求

package com.janeroad.controller;

import com.janeroad.db.MockDB;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.thymeleaf.util.StringUtils;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;

@Controller
@Slf4j
public class ServerHandler {
    /**
     * 功能描述: 第一次登录验证
     * 〈〉
     * @Param: [redirectUrl, session, model]
     * @Return: java.lang.String
     * @Author: JaneRoad
     * @Date: 2020/3/29 16:21
     */

    @RequestMapping("/checkToken")
    public String checkToken(String redirectUrl, HttpSession session, Model model, HttpServletRequest request)
    {
        //获取token
        String token = (String) session.getServletContext().getAttribute("token");
        if(StringUtils.isEmpty(token)){
            model.addAttribute("redirectUrl",redirectUrl);
            return "login";
        }else{
            //验证token
            Cookie[] cookies = request.getCookies();
            for (Cookie cookie : cookies) {
                if(cookie.getValue().equals(token)){
                    //验证通过,返回客户端
                    log.info("token验证通过");
                    return "redirect:"+redirectUrl+"?token="+token;
                }
            }
        }
        model.addAttribute("redirectUrl",redirectUrl);
        return "login";

    }

    @PostMapping("/login")
    public String login(String username,
                        String password,
                        String redirectUrl,
                        HttpSession session,
                        Model model){
        //判断登录
        if("admin".equals(username) && "123123".equals(password)){
            //1、创建token
            String token = UUID.randomUUID().toString();
            log.info("token创建成功!token={}",token);
            //2、token保存到全局会话中
            session.getServletContext().setAttribute("token",token);
            //3、token保存到数据库
            MockDB.tokenSet.add(token);
            //4、返回客户端
            return "redirect:"+redirectUrl+"?token="+token;
        }else{
            log.error("用户名密码错误!username={},password={}",username,password);
            model.addAttribute("redirectUrl",redirectUrl);
            return "login";
        }
    }

    @RequestMapping("/verify")
    @ResponseBody
    public String verifyToken(String token,String clientLogoutUrl){
        if(MockDB.tokenSet.contains(token)){
            Set<String> set = MockDB.clientLogoutUrlMap.get(token);
            if(set == null){
                set = new HashSet<>();
            }
            set.add(clientLogoutUrl);
            MockDB.clientLogoutUrlMap.put(token,set);
            return "true";
        }
        return "false";
    }

    @RequestMapping("/logout")
    public String logout(HttpSession session){
        session.invalidate();
        return "login";
    }




}

6、创建监听器,登出的时候账号销毁的时候需要操作

package com.janeroad.listener;

import com.janeroad.db.MockDB;
import com.janeroad.util.HttpUtil;

import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
import java.util.Iterator;
import java.util.Set;

@WebListener
public class SessionListener implements HttpSessionListener {
    @Override
    public void sessionDestroyed(HttpSessionEvent se) {
        //1、删除全局会话中的token
        //2、删除数据库的用户信息
        //3、通知所有客户端销毁session
        String token = (String) se.getSession().getServletContext().getAttribute("token");
        se.getSession().getServletContext().removeAttribute("token");
        MockDB.tokenSet.remove(token);
        Set<String> set = MockDB.clientLogoutUrlMap.get(token);
        Iterator<String> iterator = set.iterator();
        while(iterator.hasNext()){
            HttpUtil.sendHttpRequest(iterator.next(),null);
        }
        MockDB.clientLogoutUrlMap.remove(token);
    }
}

7、创建监听器配置ListenerConfig

package com.janeroad.config;

import com.janeroad.listener.SessionListener;
import org.springframework.boot.web.servlet.ServletListenerRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class ListenerConfig implements WebMvcConfigurer {

    @Bean
    public ServletListenerRegistrationBean bean(){
        ServletListenerRegistrationBean bean = new ServletListenerRegistrationBean();
        bean.setListener(new SessionListener());
        return bean;
    }

}

8、创建MockDB模拟数据库

package com.janeroad.db;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

public class MockDB{
    //记录token
    public static Set<String> tokenSet=new HashSet<>();
    //客户端登出地址
    public static Map<String,Set<String>> clientLogoutUrlMap = new HashMap<>();
}

9、创建HttpUtil工具类

package com.janeroad.util;

import org.springframework.util.StreamUtils;

import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.Map;

public class HttpUtil {
    /**
     * id=1
     * name=tom
     * {id=1,name=tom} id=1&name=tom
     * @param httpUrl
     * @param params
     * @return
     */
    public static String sendHttpRequest(String httpUrl, Map<String,String> params){
        try {
            URL url = new URL(httpUrl);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.setRequestMethod("POST");
            connection.setDoOutput(true);
            if(params!=null && params.size()>0){
                StringBuffer stringBuffer = new StringBuffer();
                for(Map.Entry<String,String> entry:params.entrySet()){
                    stringBuffer.append("&")
                            .append(entry.getKey())
                            .append("=")
                            .append(entry.getValue());
                }
                connection.getOutputStream().write(stringBuffer.substring(1).toString().getBytes("UTF-8"));
            }
            connection.connect();
            String response = StreamUtils.copyToString(connection.getInputStream(), Charset.forName("UTF-8"));
            return response;
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e){
            e.printStackTrace();
        }
        return null;
    }
}

SSO_Client——Tmall

天猫客户端和淘宝的相似

1、首先配置淘宝客户端的配置文件application.yml(访问端口、thymeleaf 模板、前后缀、标头、编码)

server:
  port: 8082
spring:
  thymeleaf:
    prefix: classpath:/templates/
    suffix: .html
    servlet:
      content-type: text/html
    encoding: UTF-8

2、resources下创建templates目录,写入index.html静态页面

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" th:href="@{/layui/css/layui.css}" media="all">
</head>
<body>
<div class="layui-container" style="width: 700px;height: 600px;margin-top: 0px;padding-top: 60px;">

    <h1>天猫首页</h1>

    <div style="margin-left: 460px; width: 200px;">
        欢迎回来!admin
        <a th:href="${serverLogoutUrl}">
            <button class="layui-btn layui-btn-warm layui-btn-radius">退出</button>
        </a>
    </div>

    <img width="700px" th:src="@{/images/tmall.png}">

</div>
</body>
</html>

3、static目录导入layui,layui是一个前端框架。image下导入图片,模拟天猫首页

4、创建运行类TmallApplication

package com.janeroad;


import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class TmallApplication {
    public static void main(String[] args) {
        SpringApplication.run(TmallApplication.class,args);
    }
}

5、创建控制器 TmallHandler,处理异步请求

package com.janeroad.controller;

import com.janeroad.util.SSOClientUtil;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.servlet.http.HttpSession;

@Controller
public class TmallHandler {

    @GetMapping("/tmall")
    public String index(Model model){
        model.addAttribute("serverLogoutUrl", SSOClientUtil.getServerLogoutUrl());
        return "index";
    }

    @RequestMapping("/logout")
    public String logout(HttpSession session){
        session.invalidate();
        return "redirect:/tmall";
    }
}

6、创建拦截器TmallInterceptor,继承与HandelerInterceptor,用于当请求服务端页面时判断用户是否登录,如果登录过放行,如果未登录跳转登录页面

package com.janeroad.interceptor;

import com.janeroad.util.HttpUtil;
import com.janeroad.util.SSOClientUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.HashMap;

@Slf4j
public class TmallInterceptor implements HandlerInterceptor {
    /**
     * true 放行
     * false 不放行
     *
     * @param request
     * @param response
     * @param handler
     * @return
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //1、判断是否已经登录
        HttpSession session = request.getSession();
        Boolean isLogin = (Boolean) session.getAttribute("isLogin");
        if (isLogin != null && isLogin) {
            return true;
        }

        //2、判断token
        String token = request.getParameter("token");
        if (!StringUtils.isEmpty(token)) {
            //验证token
            log.info("token存在,需要验证");
            //发起验证
            String httpUrl = SSOClientUtil.SERVER_HOST_URL + "/verify";
            HashMap<String, String> params = new HashMap<>();
            params.put("token", token);
            params.put("clientLogoutUrl",SSOClientUtil.getClientLogoutUrl());
            String isVerify = HttpUtil.sendHttpRequest(httpUrl, params);
            if ("true".equals(isVerify)) {
                log.info("token验证通过,token={}", token);
                //token保存到本地Cookie
                Cookie cookie = new Cookie("token", token);
                response.addCookie(cookie);
                session.setAttribute("isLogin", true);
                return true;
            }
        }

        //3、跳转到认证中心进行登录
        SSOClientUtil.redirectToCheckToken(request, response);
        return false;
    }
}

7、创建工具类SSOClientUtil

package com.janeroad.util;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Properties;

public class SSOClientUtil {
    private static Properties properties = new Properties();
    public static String SERVER_HOST_URL;
    public static String CLIENT_HOST_URL;

    static {
        try {
            properties.load(SSOClientUtil.class.getClassLoader().getResourceAsStream("sso.properties"));
            SERVER_HOST_URL = properties.getProperty("server.host.url");
            CLIENT_HOST_URL = properties.getProperty("client.host.url");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //跳转到认证中心
    public static void redirectToCheckToken(HttpServletRequest request, HttpServletResponse response) {
        StringBuffer url = new StringBuffer();
        url.append(SERVER_HOST_URL)
                .append("/checkToken?redirectUrl=")
                .append(CLIENT_HOST_URL)
                .append(request.getServletPath());
        try {
            response.sendRedirect(url.toString());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static String getServerLogoutUrl(){
        return SERVER_HOST_URL+"/logout";
    }

    public static String getClientLogoutUrl(){
        return CLIENT_HOST_URL+"/logout";
    }
}

创建工具类HttpUtil

package com.janeroad.util;

import org.springframework.util.StreamUtils;

import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.Map;

public class HttpUtil {
    /**
     * id=1
     * name=tom
     * {id=1,name=tom} id=1&name=tom
     * @param httpUrl
     * @param params
     * @return
     */
    public static String sendHttpRequest(String httpUrl, Map<String,String> params){
        try {
            URL url = new URL(httpUrl);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.setRequestMethod("POST");
            connection.setDoOutput(true);
            if(params!=null && params.size()>0){
                StringBuffer stringBuffer = new StringBuffer();
                for(Map.Entry<String,String> entry:params.entrySet()){
                    stringBuffer.append("&")
                            .append(entry.getKey())
                            .append("=")
                            .append(entry.getValue());
                }
                connection.getOutputStream().write(stringBuffer.substring(1).toString().getBytes("UTF-8"));
            }
            connection.connect();
            String response = StreamUtils.copyToString(connection.getInputStream(), Charset.forName("UTF-8"));
            return response;
        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e){
            e.printStackTrace();
        }
        return null;
    }
}

8、resources下写入一个配置文件sso.properties,用于记录服务端地址

server.host.url=http://localhost:8080
client.host.url=http://localhost:8082

最终实现效果

访问localhost://8081/taobao,模拟访问淘宝跳转到登录页面

访问localhost://8082/tmall,模拟访问天猫跳转到登录页面

淘宝输入用户名密码,跳转淘宝首页

刷新localhost://8082/tmall,天猫自动登录进入首页

在天猫和淘宝任一界面退出账号,另一个界面随之退出,从而实现一处登录处处登录,一处登出处处登出。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!