父域 Cookie实现sso单点登录

单点登录(Single Sign On, SSO)是指在同一帐号平台下的多个应用系统中,用户只需登录一次,即可访问所有相互信任的应用系统。Cookie 的作用域由 domain 属性和 path 属性共同决定。在 Tomcat 中,domain 属性默认为当前域的域名/IP地址。path 属性的有效值是以“/”开头的路径,在 Tomcat 中,path 属性默认为当前 Web 应用的上下文路径。如果将 Cookie 的 domain 属性设置为当前域的父域,那么就认为它是父域 Cookie。Cookie 有一个特点,即父域中的 Cookie 被子域所共享,换言之,子域会自动继承父域中的Cookie。利用 Cookie 的这个特点,不难想到,将 Session ID(或 Token)保存到父域中不就行了。没错,我们只需要将 Cookie 的 domain 属性设置为父域的域名(主域名),同时将 Cookie 的 path 属性设置为根路径,这样所有的子域应用就都可以访问到这个 Cookie 了。不过这要求应用系统的域名需建立在一个共同的主域名之下,如 http://tieba.baidu.com 和 http://map.baidu.com,它们都建立在 http://baidu.com 这个主域名之下,那么它们就可以通过这种方式来实现单点登录。

配置host文件

父域 Cookie实现sso单点登录_第1张图片

项目结构

父域 Cookie实现sso单点登录_第2张图片

引入依赖

父项目引入依赖,子项目无依赖引入

         
            org.springframework.boot
            spring-boot-starter-web
        
        
            org.projectlombok
            lombok
            1.18.24
        
        
            org.springframework.boot
            spring-boot-starter-thymeleaf
            2.6.7
        
        
            javax.servlet
            javax.servlet-api
            4.0.1
        

login系统

前端部分

login.html页面,负责显示登录页面并提交登录




    
    Login Module


    

用户登录

姓名:

密码:

后端部分

跳转登录页面 

import com.sso.login.pojo.User;
import com.sso.login.utils.LogCacheUtil;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.CookieValue;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.thymeleaf.util.StringUtils;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpSession;

@Controller
@RequestMapping("/view")
public class ViewController {
    /**
     * 跳转到登录页面
     * @return
     */
    @GetMapping("/login")
    public String toLogin(@RequestParam(required = false, defaultValue = "") String target,
                          HttpSession session,
                          @CookieValue(required = false, value = "TOKEN") Cookie cookie){
        if(StringUtils.isEmpty(target)){
            target = "login.codeshop.com:9010";
        }
        if(cookie != null){
            // 从父域拿到token
            String value = cookie.getValue();
            User user = LogCacheUtil.loginUser.get(value);
            if(user != null){
                return "redirect:" + target;
            }
        }
        session.setAttribute("target",target);
        return "login";
    }

}

负责登录校验以及解析token 

import com.sso.login.pojo.User;
import com.sso.login.utils.LogCacheUtil;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.CookieValue;
import org.springframework.web.bind.annotation.GetMapping;
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.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;

@Controller
@RequestMapping("/login")
public class LoginController {

    private static Set dbUser;
    // 模拟数据库
    static {
        dbUser = new HashSet<>();
        dbUser.add(new User(1, "zxb", "1234567"));
        dbUser.add(new User(2, "admin", "123456"));
    }

    @PostMapping
    public String doLogin(User user, HttpSession session, HttpServletResponse response) {
        String target = (String) session.getAttribute("target");
        // 验证登录
        Optional first = dbUser.stream().filter(dbUser -> dbUser.getUsername().equals(user.getUsername()) &&
                dbUser.getPassword().equals(user.getPassword()))
                .findFirst();
        if (first.isPresent()) {
            String token = UUID.randomUUID().toString();
            // 保存token到父域 Cookie
            Cookie cookie = new Cookie("TOKEN", token);
            cookie.setPath("/");
            cookie.setDomain("codeshop.com");
            response.addCookie(cookie);
            // 登录成功保存到LogCacheUtil工具类中,这个部分可以用redis去实现
            LogCacheUtil.loginUser.put(token, first.get());
        } else {
            session.setAttribute("msg", "用户名或密码错误!");
            // 错误跳转到登录页面
            return "login";
        }
        // 重定向到目标地址
        return "redirect:" + target;
    }

    @GetMapping("info")
    @ResponseBody
    public ResponseEntity getUserInfo(String token) {
        if (!StringUtils.isEmpty(token)) {
            // 验证token并从token获取用户信息并返回
            User user = LogCacheUtil.loginUser.get(token);
            return ResponseEntity.ok(user);
        } else {
            return new ResponseEntity<>(null, HttpStatus.BAD_REQUEST);
        }
    }

    @GetMapping("/logout")
    public String loginOut(@CookieValue(value = "TOKEN") Cookie cookie, HttpServletResponse response, String target) {
        cookie.setMaxAge(0);
        LogCacheUtil.loginUser.remove(cookie.getValue());
        response.addCookie(cookie);
        return "redirect:" + target;
    }
}

model

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;

@Data //添加getter/setter
@NoArgsConstructor  //添加无参构造器
@AllArgsConstructor //添加全参构造器
@Accessors(chain = true) //添加链式调用
public class User {
    private Integer id;
    private String username;
    private String password;
}

保存User工具类

import com.sso.login.pojo.User;

import java.util.HashMap;
import java.util.Map;

public class LogCacheUtil {
    public static Map loginUser = new HashMap<>();

}

Main系统

前端部分

index.html页面 




    
    Index Module


    

欢迎登录!

login logout

已登录!



id:

username:

后端部分 

Viewcontroller

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.CookieValue;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.client.RestTemplate;
import org.thymeleaf.util.StringUtils;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpSession;
import java.util.Map;

@Controller
@RequestMapping("/view")
public class ViewController {

    @Autowired
    private RestTemplate restTemplate;

    private final String USER_INFO_ADDRESS = "http://login.codeshop.com:9000/login/info?token=";

    @GetMapping("/index")
    public String toIndex(@CookieValue(required = false, value = "TOKEN") Cookie cookie,
                          HttpSession session) {
        if (cookie != null) {
            String token = cookie.getValue();
            if (!StringUtils.isEmpty(token)) {
                Map result = restTemplate.getForObject(USER_INFO_ADDRESS + token, Map.class);
                session.setAttribute("loginUser", result);
            }
        }
        return "index";
    }
}

配置restTemplate的HTTP客户端工具

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

    @Bean
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
}

cart系统

前端部分

index.html页面




    
    Blog Module


    

登录页面 !

login logout

已登录!



id:

username:

后端部分 

view的controller

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.CookieValue;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.client.RestTemplate;
import org.thymeleaf.util.StringUtils;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpSession;
import java.util.Map;

@Controller
@RequestMapping("/view")
public class ViewController {
    @Autowired
    private RestTemplate restTemplate;

    private final String USER_INFO_ADDRESS = "http://login.codeshop.com:9000/login/info?token=";

    @GetMapping("/index")
    public String toIndex(@CookieValue(required = false, value = "TOKEN") Cookie cookie,
                          HttpSession session){
        if(cookie != null){
            // 从cookie拿到的token去请求获取用户信息需要去实现验证
            String token = cookie.getValue();
            if(!StringUtils.isEmpty(token)){
                Map result = restTemplate.getForObject(USER_INFO_ADDRESS+token,Map.class);
                session.setAttribute("loginUser",result);
            }
        }
        return "index";
    }
}

配置restTemplate的HTTP客户端工具

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

@SpringBootApplication
public class CartApp {
    public static void main(String[] args) {
        SpringApplication.run(CartApp.class,args);
    }
    
    @Bean
    public RestTemplate restTemplate(){
        return new RestTemplate();
    }
}

测试

启动

父域 Cookie实现sso单点登录_第3张图片

访问 mainApp

父域 Cookie实现sso单点登录_第4张图片

登录

父域 Cookie实现sso单点登录_第5张图片 登录成功

父域 Cookie实现sso单点登录_第6张图片

输入cart.codeshop.com:9012/view/index,直接访问cart系统成功

父域 Cookie实现sso单点登录_第7张图片 输入www.codeshop.com:9010/view/index,直接访问main系统成功父域 Cookie实现sso单点登录_第8张图片

你可能感兴趣的:(工作问题总结,firefox,chrome,前端,java)