背景:

甲方爸爸们越来越重视系统的安全性,我方系统本来采用账户+密码形式登录的(其他也支持)。现在要求登录必须对接专门的密评公司,第一阶段为采用u_key也就是插优盘登录。后续考虑再做其他形式。

一开始不了解这块东西,觉得挺难的。等拿到密评公司的相关文档后,基于目前系统进行改造。整个过程还算顺利,中间由于不清楚一些接口的定义,和三方的人员沟通过几次。

要的效果:

系统登陆时,插入ca锁,输入ca密码,完成校验后即可登录成功。

实现过程:

UKEY证书登录的流程为:

  1. 客户端从服务端获取随机数并生成签名值(raw裸签名)
  2. 将签名值数据传给业务服务端
  3. 业务服务端将讲客户端传过来的签名数据调用密码服务接口验签
  4. 返回验签结果,根据验签结果运行客户端登陆

设计:
1、用户信息保存在我方系统中,在用户管理页面,为用户关联UKEY锁。

下载ca锁驱动并安装,插入锁,读取锁的sn序列号,与用户信息绑定,保存在数据库中。sn序列号和用户一对一关系。(前端进行读取,相关操作及代码三方提供有参考文档)

2、改造登录 插入ca锁,输入锁的密码后进行登录。
2.1前端进行ca的基本校验操作,均可参考三方文档实现。对ca密码、序列号的校验。无误后根据密码+序列号+随机数(用于加密的原文),生成签名数据certData以及签名证书ceertSign,然后将签名、密码、certData、certSign传到后端进行处理。

2.2后端基于公司的现有的框架做的处理,相当于对登录做了拦截。
后端获取到传递的参数,进行数据的校验,通过后根据 序列号sn查询user表,查询当前锁是否关联用户信息,否则结束并提示。是则进行验签操作。
验签有专门的接口,只需根据接口文档进行调用。验签若成功,则可以让当前用户登录成功,反之则失败。

相关代码展示在后面:
在这里插入图片描述
前端相关代码

function check(loadtype) {
            debugger;
			var username = document.getElementById("username").value;
			var password = document.getElementById("password").value;
            var devList = "";
            var devName = "";
            var signData = "";
            var signCert = "";
			if (1 == loadtype) {
				username = checkUsbKey();
                password = document.getElementById("passwordca").value;
				if (username==false){
                    return false;
                }
				if (password == "") {
					epoint.alert("CA密码不能为空");
					return false;
				}
				//验证
                devList = mToken.Hunca_GetUserListGN();	//枚举Ukey,获取证书G项信息列表
                if(null == devList){
                    alert(mToken.Hunca_GetLastError());
                    return false;
                }
                devName = devList[0];

                userName = mToken.Hunca_userNameGNToUserName(devName);
                if(!mToken.Hunca_PKCS1(password,userName,password)){
                    alert(mToken.Hunca_GetLastError());
                    return false;
                }
                //获取签名结果
                signData = mToken.Hunca_GetSignData();
                //签名证书
                signCert = mToken.Hunca_GetSignCert();

			} else {
				if (username == "") {
					epoint.alert("用户名不能为空");
					return false;
				}
				if (password == "") {
					epoint.alert("密码不能为空");
					return false;
				}
			}

			username = epoint.encodeUtf8(username);
			username = sm2Encrypt(username, sm2PubKey, 0);

			password = epoint.encodeUtf8(password);
			password = sm2Encrypt(password, sm2PubKey, 0);
			var verifycode = "";
			
			<!-- 如果需要验证码,则打开下面这段注释 -->
			<!-- begin -->
			 var codeinput = $.trim($codeinput.val());
			/* if (!codeinput) {
				result = false;
				epoint.alert('验证码不能为空!');
				return false;
			} */
			
			if (!$("#verifycode-box").is(":hidden")) {
				if (!codeinput) {
					result = false;
					epoint.showTips('验证码不能为空!', {
						state : 'warning'
					});
					return false;
				}
			}
			var codedata = $unvisualcode.val();

			verifycode = codeinput + '#' + codedata;  
			<!-- end -->
			
			userName_securityverification=username;
		    passWord_securityverification=password;
			verifycode_securityverification=verifycode;
		    rememberMe_securityverification='';
		    loginType_securityverification=loadtype;
            sign_securityverification = signData;
			// 登录
            if(loadtype=='1'){
                epoint.execute('loginaction.cacustomlogin', '', [ username, password,
                    loadtype, signData, signCert ], checkMultipleLogin);
            }else {
                epoint.execute('loginaction.login', '', [ username, password,
                    loadtype, '', verifycode ], checkMultipleLogin);
            }
			return true;
		}
        var mToken = new hunca_mToken_core();
		function checkUsbKey() {
            //第一步:获取证书名称
            var devList = "";
            var selectDev = null;
            //枚举Ukey,获取证书G项信息列表
            devList = mToken.Hunca_GetUserListGN();
            if(null == devList){
                epoint.showTips(mToken.Hunca_GetLastError(),{state : 'warning'});
                return false;
            }
            selectDev = devList[0];


            //第二步 获取证书序号
            var userNameGN= null,userName = "";
            var caSN = "";

            userNameGN = selectDev;
            userName = mToken.Hunca_userNameGNToUserName(userNameGN);	//将证书G项信息转化为证书名称

            if(!mToken.Hunca_DealCertInfo(userName)){
                epoint.showTips(mToken.Hunca_GetLastError(),{state : 'warning'});
                return false;
            }

            caSN = mToken.Hunca_GetSignCertSn();
            // document.getElementById("ca_sn").value = caSN;

			return caSN;
		}

后台代码:

/**
 * @author lqfeng
 * @desc 个性登录流程接口
 * @date 2022/7/19 11:32
 */
public class CqCaCustomIdentityBuilder extends FrametIdentityBuilder {

    private static final Logger log = LoggerFactory.getLogger(CqCaCustomIdentityBuilder.class);



    @Override
    public AuthenticationToken build(ServletRequest request, ServletResponse response) {
        AuthenticationToken authenticationToken = null;
        CqCaCustomIdentityService caCustomIdentityService = new CqCaCustomIdentityService();
        String username = "";
        String password = "";
        String loadType = "0";
        String signData = "";
        String signCert = "";
        boolean rememberMe = false;
        String multiParam = "";
        FrameUser user = null;

        log.info("开始执行自定义build");
        //1、获取传递的参数
        String cmdparams = WebUtils.getCleanParam(request, "cmdParams");

        if (StringUtil.isNotBlank(cmdparams)) {
            //参数格式处理
            if (cmdparams.startsWith("[")) {
                cmdparams = cmdparams.substring(1);
            }

            if (cmdparams.endsWith("]")) {
                cmdparams = cmdparams.substring(0, cmdparams.length() - 1);
            }
            //转为参数数组
            String[] arr = cmdparams.split(",");
            log.info("前端传递的参数为:{}",arr);
            username = this.format(arr[0]);
            password = this.format(arr[1]);
            if (arr.length > 2) {
                loadType = this.format(arr[2]);
            }
            if (arr.length > 3) {
                signData = this.format(arr[3]);
            }
            if (arr.length > 4) {
                signCert = this.format(arr[4]);
            }
        }
        //2、根据loadType 类型,判断是否走个性化ca登录处理
        if (loadType.equals("1")){
            log.info("当前为ca登录。。。");

            //2.1获取CA密码 解密
            username = this.decode(username);
            password = this.decode(password);
            log.debug("解析后的sn为{},password为:{}",username,password);

            //2.2用户名或密码不能为空
            if (StringUtil.isBlank(username)||StringUtil.isBlank(password)){
                return errorToken("用户名或密码不能为空", response);
            }


            //2.4根据sn判断ca锁是否绑定用户
            user = caCustomIdentityService.isBindUser(username);
            if (user==null||StringUtil.isBlank(user.getLoginId())){
                //当前CA锁未绑定用户
                return errorToken("当前CA锁未绑定用户", response);
            }
            //
            //2.3 验签判断
            Boolean certVaild = caCustomIdentityService.judgetSignVaild(signData,signCert,password);
            if (!certVaild){
                return errorToken("签名验证失败,请确认uKey试过正确", response);
            }
        }

        Map<String, String> extendMap = new HashMap();
        Identity identity = createCommonToken(user.getLoginId(),user.getPassword(),"1",request,response,"cacustomlogin");

        return identity;


    }
    private AuthenticationToken errorToken(String msg, ServletResponse response)
    {
        Map params = new HashMap();
        params.put("systemMessages", msg);
        addCallBackMessageWithAjax(params, (HttpServletResponse)response);
        return null;
    }
}
//raw验签
    public Boolean judgetSignVaild(String signData,String signCert,String password) {
        String url = new FrameConfigService9().getFrameConfigValueByNameWithDefault("SIGN_VERIFY_RAW_URL", CaConstant.SIGN_VERIFY_RAW_URL);
        Map tokenHeader = CertUile.getTokenHeader();

        Map params = new HashMap<>();
        String appId = new FrameConfigService9().getFrameConfigValueByNameWithDefault("CA_appId", CaConstant.CA_APP_ID);
       String password1= Base64.getEncoder().encodeToString(password.getBytes());
        String password2= Base64.getEncoder().encodeToString(password1.getBytes());

        //原文
        params.put("plainText",password2);
        //签名结果
        params.put("signedText", signData);
        //签名证书
        params.put("cert", signCert);
        //时间戳
        params.put("tsaText", System.currentTimeMillis());
        //算法
        params.put("digestAlg", null);
        //密钥标识
        params.put("keyId", appId);
        String s = JsonUtil.objectToJson(params);
        log.info("body参数为:{}",s);
        String result = HttpUtil.doHttp(url, (Map<String, String>) tokenHeader, s, "POST", 2);
        log.info("raw验签接口返回结果为:{}",result);
        if (result.contains("成功")){
            return true;
        }
        return false;
    }

工具类

/**
 * @author lqfeng
 * @desc 密评util
 * @date 2022/7/20 11:22
 */
public class CertUile {
    private static final Logger log = LoggerFactory.getLogger(CertUile.class);
    //获取token
    public static String getToken() {
        String token = "";
        String hmac = "";

        //获取参数值,可配置为系统参数
        String appId = new FrameConfigService9().getFrameConfigValueByNameWithDefault("CA_appId", CaConstant.CA_APP_ID);
        String secretKey = new FrameConfigService9().getFrameConfigValueByNameWithDefault("CA_appSecretKey", CaConstant.CA_APP_SECRETKEY);
        String random = String.valueOf(System.currentTimeMillis());
        String plainText = appId + "||" + random + "||" + secretKey;
        byte[] hmacbyte = HmacUtils.encodeHmacSHA256(plainText.getBytes(), secretKey.getBytes());
        hmac = Base64.getEncoder().encodeToString(hmacbyte);
        log.info("HmacSHA256加密后的hmac值为:{}",hmac);

        Map headerMap = new HashMap<>();
        headerMap.put("appId", appId);

        Map paramMap = new HashMap<>();
        paramMap.put("random",random );
        paramMap.put("hmac",hmac );
        String jsonParams = JsonUtil.objectToJson(paramMap);
        log.info("请求参数header:{},bodyParam:{}",headerMap,paramMap);
        //请求token接口
        String result = HttpUtil.doHttp(CaConstant.APP_TOKEN_URL, (Map<String, String>) headerMap, jsonParams, "POST", 2);
        if (result.contains("成功")){
            Map<String, Object> map = JsonUtil.jsonToMap(result);
            Map<String, String> data = (Map<String, String>) map.get("data");
            token = data.get("token");
            log.info("获取到的token值为:{}",token);
        }
        return token;
    }
    //获取认证header
    public static Map getTokenHeader(){
        String appId = new FrameConfigService9().getFrameConfigValueByNameWithDefault("CA_appId", CaConstant.CA_APP_ID);
        String token = getToken();
        String random = String.valueOf(System.currentTimeMillis());
        String nonce = UUID.randomUUID().toString().replaceAll("-", "");

        Map headerMap = new HashMap<>();
        headerMap.put("appId",appId);
        headerMap.put("token",token);
        headerMap.put("random",random);
        headerMap.put("nonce",nonce);
        return headerMap;
    }
}
Logo

惟楚有才,于斯为盛。欢迎来到长沙!!! 茶颜悦色、臭豆腐、CSDN和你一个都不能少~

更多推荐