SSO单点登录

SSO单点登录:
​ SSO英文全称Single Sign On。SSO是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。它包括可以将这次主要的登录映射到其他应用中用于同一个用户的登录的机制。它是目前比较流行的企业业务整合的解决方案之一

  • 在之前的单机环境中,我们通常是将用户登陆过的信息保存在服务端的Seesion中,通过判断Session中是否存在用户信息来确认用户是否登录。
  • 在分布式环境中,以上方案就行不通了,因为分布式环境通常是由多台web服务器协调工作。而用户的登陆信息(Session)只能保存在一台web服务器中,并且这个登陆信息(Seesion)对于其他服务器是不可见的。
    举个栗子,当一个用户需要访问A系统与B系统,用户首先访问A系统并且登陆,随后用户访问B系统,但是B系统中依然认定当前是未登录状态,所以依然会让用户进行登录。

实现原理:将SSO独立作为一个子系统管理用户信息,其他应用通过此系统确认用户的登陆状态。此系统应实现的功能:注册、登陆、验证
响应类:

/**
 * fileName:LoginResp
 * description:
 * author:hcq
 * createTime:2019-03-23 10:34
 */
                                                 
public class LoginResp {  //许多博主用E3Result命名,不明白含义,请各位大佬不吝告知
    private Integer code;//状态码
    private String massage;//提示信息
    private String token;//Token
    private Object data;//用户信息
    private LoginResp(Integer code, String massage, Object data){
        this.code=code;
        this.massage=massage;
        this.data=data;
    }
    public static LoginResp build(int code, String massage, Object data){
        return new LoginResp(code,massage,data);
    }
     ......省略getset
    public LoginResp setToken(String token) {
        this.token = token;
        return this;
    }
}

控制层:
Login

@Controller
public class LoginController {
    @Value("${TOKEN_KEY}")
    private String TOKEN_KEY;
    @Autowired
    LoginService service;

    @RequestMapping("/loginPage")
    public String showLogin(){ //向其他应用提供的登陆页面
        return  "/html/login.html";
    }
    @RequestMapping("/login")
    @ResponseBody
    public LoginResp login(String name, String password,HttpServletResponse response){
        LoginResp resp = service.Login(name, password);
        if(resp.getCode()==200){//判断用户是否登录成功
            String token = resp.getToken();
response.addCookie(new Cookie(TOKEN_KEY,token));// 将token保存在cookie中,浏览器关闭即失效(类似sessionid)
        }
        return resp;
    }
}

Token验证

/**
 * fileName:TokenController
 * description: 根据token查询用户信息
 * author:hcq
 * createTime:2019-03-23 10:47
 */
@RestController
public class TokenController {
    @Autowired
    TokenService service;
    @RequestMapping("/user/token")
    public Object getUserByToken(String token,String callback) {
        String resp=JSON.toJSONString(service.getData(token));
        if(callback!=null){//跨域请求
            resp=callback+"("+resp+")";
        }
        return resp;
    }
}

服务层:
Login Service

@Service
public class LoginServiceImpl implements LoginService {
    @Autowired
    JedisUtil jedis;
    @Autowired
    UserDao dao;
    @Override
    public LoginResp Login(String name, String password) {
         LoginResp resp=null;
         Map user=dao.login(name,password);//查询数据库
         if(user!=null){
              //生成Token
               String token= UUID.randomUUID().toString();
               user.remove("upass");
               //将用户信息存入redis,并设置过期时间
               jedis.putObject("Session:"+token,user);
               jedis.expire("SESSION:" + token,30*60);

            resp=LoginResp.build(200,"登陆成功",null).setToken(token);
         }else{//登陆失败
             resp=LoginResp.build(400,"用户名或密码错误",null);
         }
        return resp;
    }
}

Token Service

@Service
public class TokenServiceImpl implements TokenService {
    @Autowired
    JedisUtil jedis;
    @Override
    public Object getData(String token) {
        String user_json= (String) jedis.getObject("Session:"+token);//通过token获取用户信息
        System.out.println(user_json);
          if(user_json==null||user_json.equals("")){
              return LoginResp.build(201, "用户登录信息已过期",null);
          }else{
              //重置过期时间
              jedis.expire(token,30*60);
              return LoginResp.build(200, "用户已登录",JSON.parseObject(user_json));
          }
    }
}

表现层:
表现层需要通过js获取cookie信息并请求SSO系统判断用户是否登陆。我将此步骤封装为了一个js。在需要验证的页面导入js并创建回调函数即可
Home.html

<html>
<head>
    <meta http-equiv="content-type" content="text/html; charset=utf-8">
    <title>Title</title>
</head>
<body>
</body>
</html>
 <script src="/js/jquery-1.8.3.min.js"></script>
 <script src="/js/check_login.js"></script>
 <script>
    function UserInfoCallback(data){//回调函数 data:用户信息
        $("body").html(JSON.stringify(data))
    }
</script>

check_login.js

var token=getCookie("TOKEN_KEY"); //获取Token信息
$(function(){
    $.ajax({
        url:"http://localhost:8080/user/token",
        type:'get',
        data:{
            token:token
        },dataType:"jsonp",
        success:function(data){
            if(parseInt(data.code)!=200){//登陆失败
                window.location="http://localhost:8080/loginPage"
            }else{
                UserInfoCallback(data.data);//回调函数,用户返回用户信息
            }
        }
    })
})
//获取cookie
function getCookie(cookieName){
    var cookieValue="";
    if (document.cookie && document.cookie != '') {
        var cookies = document.cookie.split(';');
        for (var i = 0; i < cookies.length; i++) {
            var cookie = cookies[i];
            if (cookie.substring(0, cookieName.length + 2).trim() == cookieName.trim() + "=") {
                cookieValue = cookie.substring(cookieName.length + 2, cookie.length);
                break;
            }
        }
    }
    return cookieValue;
}

请求流程: 用户第一次请求SSO系统,登陆成功后返回给客户端一个Cookie,以后的每次请求都会携带着这个Cookie(Token),在需要认证的页面中引入js,ajax拿着这个Token请求SSO系统查找用户登陆信息并返回,

总结: 其原理就是模拟了服务器端Seesion的实现,只不过是将Session保存在了redis中使多个应用可以共享,

感谢两位大佬的文章:
https://blog.csdn.net/qq_39056805/article/details/80789008
https://www.cnblogs.com/yueshutong/p/9468035.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

敲得码黛

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值