• 实现第三方系统单点登录


    思路举例:

      以QQ为例:pc端QQ界面中所有的第三方软件系统,以QQ邮箱举例,

      QQ中成功登陆,从而点击QQ邮箱可以直接打开网页进入邮箱并无需登录

      在这里就使用了单点登录,QQ邮箱就是QQ信任的第三方系统,双方达成协议,

      双方需要把加密方式,和加密串达成一致。当然如果公司是中型企业,加密串后期

      会有运维去和客户沟通。 接下来贴代码。

    代码实例:

      首先设置一个加密串,这里是在properties文件中定义,嫌麻烦可以直接在接口中定义静态私有属性

      #秘钥
      AtsecretKey=Activity0CoJUm6123456789
      

      
     //引入秘钥 
    @Value("${AtsecretKey}") private String AtsecretKey; /** * 根据请求的参数加密判断 * @param currTime * @param idcardno * @param type * @param signature * @return */ @RequestMapping("/token") @ResponseBody public ResultEntity checkToken(String currTime,String idcardno,String type,String signature ){ ResultEntity resultEntity=new ResultEntity(); Map<String,String> desMap=new HashMap<>(); desMap.put("currTime",currTime); desMap.put("idcardno",idcardno); desMap.put("type",type); StringBuffer sb = new StringBuffer(); StringBuffer sbkey = new StringBuffer(); Set es = desMap.entrySet(); //所有参与传参的参数按照accsii排序(升序) Iterator it = es.iterator(); while(it.hasNext()) { Map.Entry entry = (Map.Entry)it.next(); String k = (String)entry.getKey(); Object v = entry.getValue(); //空值不传递,不参与签名组串 if(null != v && !"".equals(v)) { sb.append(v); sbkey.append(v); } } sbkey=sbkey.append(AtsecretKey); System.out.println("字符串:"+sbkey.toString()); String md5Item = MD5Util.md5(sbkey.toString()); System.out.println("MD5加密值:"+md5Item); System.out.println(sb.toString()+"sign="+md5Item); //拼字符串后加密 if (md5Item.equals(signature)){ long nowTime=System.currentTimeMillis(); if (((nowTime - Long.parseLong(currTime))*1.0 /(1000 * 60))>20){ resultEntity.setState(HttpCode.FAILED); resultEntity.setRetMessage("认证时间超时"); return resultEntity; } DESPlus desPlus=new DESPlus(); String strJson=JsonUtils.toJson(desMap); String desMessage=desPlus.encrypt(strJson); resultEntity.setState(HttpCode.SUCCESS); resultEntity.setMessage("认证通过"); resultEntity.setResult(desMessage); return resultEntity; } resultEntity.setState(HttpCode.FAILED); resultEntity.setMessage("请求失败"); return resultEntity; }
    代码解释:
      ResultEntity:工具类,用于封装json数据,返回页面响应请求
      currTime:时间戳
      idcardno:身份证号
      type:设备类型手机或者pc  
       signature:加密串,

      
    Map<String,String> desMap=new HashMap<>();
            desMap.put("currTime",currTime);
            desMap.put("idcardno",idcardno);
            desMap.put("type",type);

    将参数放入map中,方便接下来排序

    StringBuffer sb = new StringBuffer();
            StringBuffer sbkey = new StringBuffer();
            Set es = desMap.entrySet();  //所有参与传参的参数按照accsii排序(升序)
            Iterator it = es.iterator();
            while(it.hasNext()) {
                Map.Entry entry = (Map.Entry)it.next();
                String k = (String)entry.getKey();
                Object v = entry.getValue();
                //空值不传递,不参与签名组串
                if(null != v && !"".equals(v)) {
                    sb.append(v);
                    sbkey.append(v);
                }
    sbkey=sbkey.append(AtsecretKey);
    System.out.println("字符串:"+sbkey.toString());
    String md5Item = MD5Util.md5(sbkey.toString());
    System.out.println("MD5加密值:"+md5Item);
    System.out.println(sb.toString()+"sign="+md5Item);
    
    
    
    
    这里需要用的accsii排序,accsii详细解释可以去查一下
    通过迭代器迭代排序,并且清除空值,然后将值封装好key,value
    另外在排序后的值后面加上加密串(
    AtsecretKey),生成加密签名
    接着使用md5工具类加密参数
    //拼字符串后加密
            if (md5Item.equals(signature)){
                long nowTime=System.currentTimeMillis();
                if (((nowTime - Long.parseLong(currTime))*1.0 /(1000 * 60))>20){
                    resultEntity.setState(HttpCode.FAILED);
                    resultEntity.setRetMessage("认证时间超时");
                    return resultEntity;
                }
                DESPlus desPlus=new DESPlus();
                String strJson=JsonUtils.toJson(desMap);
                String desMessage=desPlus.encrypt(strJson);
                resultEntity.setState(HttpCode.SUCCESS);
                resultEntity.setMessage("认证通过");
                resultEntity.setResult(desMessage);
                return resultEntity;
            }
            resultEntity.setState(HttpCode.FAILED);
            resultEntity.setMessage("请求失败");
            return resultEntity;
    这里将加密后得参数和第三方传的signature判断是否相同,如果相同证明是信任的第三方,
    才可以执行下面的操作
    拿到第三方发送的时间戳(currTime)并且获取当前的时间戳,设置时间戳过期时间,
    这里是防止恶意攻击,不可能第二天还可以拿着前一天的请求来访问,
    成功设置后在使用DES加密,因为des是可以解密的,将之前map的参数在一次用des加密,
    转换成json数据,让后放进工具类返回请求,第三方拿到参数,再次带着参数来访问接口,

    /**
         * 根据第三方的Token来处理单点登录
         * @param request
         * @param session
         * @return
         */
        @RequestMapping("/IntoLogin")
        public String intoLogin( HttpServletRequest request, HttpSession session,String token){
            //拿到token解密
            DESPlus desPlus=new DESPlus();
            try {
                String decToken=desPlus.decrypt(token);
                Map<String,String>map=JsonUtils.fromJson(decToken,Map.class);
                ReaderEntity record = new ReaderEntity();
                record.setCardno(map.get("cardno"));
                record.setIdcardno(map.get("idcardno"));
                String type=map.get("type");
                String currTime=map.get("currTime");
                ReaderSession readerSession =null;
                long nowTime=System.currentTimeMillis();
                //判断过期时长,请求时间超过20分钟就无效
                if (((nowTime - Long.parseLong(currTime))*1.0 /(1000 * 60))<20) {
                    readerSession = readerService.login(record, request);
                }
                if(readerSession!=null) {
                        session.setAttribute("readerInfo", readerSession);
                        UnionUserSession unionUserSession = new UnionUserSession();
                        unionUserSession.setUserType(2);
                        unionUserSession.setUserId(readerSession.getReader().getRecno().toString());
                        request.getSession().setAttribute(UnionUserSession.SESSION_ID, unionUserSession);
                        //这里判断密钥类型是pc还是移动端,登录后将跳转不同页面
                        if (type.equals("0")){
                            return "/pc/index";
                        }else{
                            return "/mobile/activity/home";
                            }
                        }
            }catch (Exception e) {
                return "/pc/index";
            }
            return "/pc/index";
        }

    这里的代码不多做解释,应该能看得懂,其实这种单点登录思路要屡清楚

    为了安全性参数一定是要加密的,特别是加密协议,密钥是双方知道的,第三方

    请求的时候也是带着参数可已经加密的参数来求接口,如果我们拿着他们的参数排序

    在加上密钥以后加密,发现加密后的字符串一致说明是我们信任的第三方,

    这里单点登录要用两个接口,所以我们需要使用des加密后返回给他们再次请求,

    一个接口负责判断是否第三方,另一个接口登录,

    这里只提供接口供第三方访问。



    
    
    
  • 相关阅读:
    Selenium快速入门(下)
    Selenium快速入门(上)
    Python中yield和yield from的用法
    Python多进程
    Spring Cloud微服务安全实战_3-5_API安全之常见问题
    Spring Cloud微服务安全实战_3-3_API安全之流控
    Spring Cloud微服务安全实战_3-2_第一个API及注入攻击防护
    Spring Cloud微服务安全实战_3-1_API安全 常见的安全机制
    Spring Cloud微服务安全实战_2-1_开发环境
    Spring Cloud微服务安全实战_1-1_导学
  • 原文地址:https://www.cnblogs.com/yyKong/p/10855551.html
Copyright © 2020-2023  润新知