单点登录的业务实现

单点登录

单点登录(Single Sign On),简称为 SSO,是比较流行的企业业务整合的解决方案之一。SSO的定义是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。一句话概括:一次登陆,处处访问。
解决:

  • 用户身份信息独立管理,更好的分布式管理。
  • 可以自己扩展安全策略
  • 跨域问题

单点登录容易造成认证服务器的压力过大,因为单点登录的核心是需要一个认证中心来验证和生成token,大量的请求容易造成服务器压力过大。

认证中心需要做的就是将请求拦截下来,验证token的有效性,如果token不正确或为空打回到登录页面重新登陆。

流程:
参考博客https://blog.csdn.net/jmkmlm123456/article/details/98343821?utm_medium=distribute.pc_relevant.none-task-blog-baidujs-2

认证中心步骤:

  1. 用接受的用户名密码核对后台数据库。
  2. 将用户信息加载到写入redis,redis中有该用户视为登录状态。
  3. 用userId+当前用户登录ip地址+密钥生成token。
  4. 重定向用户到之前的来源地址,同时把token作为参数附上。

生成token

利用JWT工具生成token,一个JWT由三个部分组成:公共部分、私有部分、签名部分。最后由这三者组合进行base64编码得到JWT。

  1. 公共部分主要是该JWT的相关配置参数,比如签名的加密算法、格式类型、过期时间等等。
  2. 私有部分是用户自定义的内容,根据实际需要真正要封装的信息。
  3. 签名部分根据用户信息+盐值+密钥生成的签名。如果想知道JWT是否是真实的只要把JWT的信息取出来,加上盐值和服务器中的密钥就可以验证真伪。所以不管由谁保存JWT,只要没有密钥就无法伪造。

验证token

用JWT解析,当业务模块某个页面要检查当前用户是否登录时,提交到认证中心,认证中心进行检查校验,返回登录状态、用户Id和用户名称。
步骤:

  1. 利用密钥和IP检验token是否正确,并获得里面的userId
  2. 用userId检查Redis中是否有用户信息,如果有延长它的过期时间。
  3. 登录成功状态返回。

业务模块的登陆检查

首先要定义一个拦截器,因为并不是任何一个模块都需要检查登录状态,通过拦截请求来确认放不放行。
定义一个注释。

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LoginRequired {

    // true代表拦截校验必须通过,false表示拦截器校验不通过也可以继续访问
    boolean loginSuccess() default true;

}

拦截器判断是否注释,如果存在注释则进行拦截判断,否则放行。

// 判断被拦截请求的访问的方法的注解(是否需要拦截的)
        // 反射得到方法的注解
        HandlerMethod handlerMethod = (HandlerMethod) handler;

        LoginRequired methodAnnotation = handlerMethod.getMethodAnnotation(LoginRequired.class);

        // 是否拦截
        if (methodAnnotation == null) {
            return true;
        }

在拦截器中定义一个token,判断是否登陆过,登陆过的用户会将token存入cookie中,而新验证的用户会通过login请求携带的参数产生新的token,二者非此即彼。

String token = "";

        // oldtoken代表之前登陆过
        String oldToken = CookieUtil.getCookieValue(request, "oldToken", true);
        if (StringUtils.isNotBlank(oldToken)) {
            token = oldToken;
        }

        // newtoken代表地址栏携带验证过后的token
        String newToken = request.getParameter("token");
        if (StringUtils.isNotBlank(newToken)) {
            token = newToken;

        }


        boolean loginSuccess = methodAnnotation.loginSuccess();// 获取该请求是否必须登陆成功,来决定拦截器拦截成功请求是否继续访问

在拦截器中远程调用验证中心,验证token的有效性,返回状态map。

 String success = "fail";
        Map<String, String> map = new HashMap<>();

        if (StringUtils.isNotBlank(token)) {

            // 调用认证中心验证

            String ip = "127.0.0.1";
            if (StringUtils.isNotBlank(request.getRemoteHost())) {
                ip = request.getRemoteHost();
            }
            String successJson = HttpclientUtil.doGet("http://127.0.0.1:8085/verify?token=" + token + "&currentIp=" + ip);
            map = JSON.parseObject(successJson, Map.class);
            success=map.get("status");

        }

验证中心的验证判断jwt解析token形成的用户信息是否存在,不存在返回fail状态,否则在map中加入解析的用户信息和success标志位。

public String verify(@RequestParam String token, @RequestParam String currentIp) {

        // 通过jwt验证

        HashMap<String, String> map = new HashMap<>();

        Map<String, Object> gmall = JwtUtil.decode(token, "gmall", currentIp);

        if (gmall != null) {

            map.put("status", "success");

            map.put("memberId", (String) gmall.get("memberId"));
            map.put("nickname", (String) gmall.get("nickname"));
        } else {
            map.put("status", "fail");
        }

        return JSON.toJSONString(map);
    }

在拦截器中判断返回的状态,失败打回登录页面重新登陆,否则将用户信息写入请求属性,更新cookie。

if (!success.equals("success")) {
                // 重定向passport登陆
                response.sendRedirect("http://127.0.0.1:8085/index?ReturnUrl=" + request.getRequestURL());
                return false;


            } else {

                // 将token携带的信息写入
                request.setAttribute("memberId", map.get("memberId"));
                request.setAttribute("nickname", map.get("nickname"));

                if (StringUtils.isNotBlank(token)) {
                    // 覆盖cookie中token
                    CookieUtil.setCookie(request, response, "oldToken", token, 60 * 60 * 2, true);
                }
            }

至此单点登录流程结束,有拦截器远程调用验证中心判断请求的合法性,通过注释实现了登不登录的区别,只需要通过注释而不是路径匹配判断,减少了代码量又增加效率。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值