安全-用户身份验证

前一阵子,线上的游戏被黑客着实折腾了一把,也给我好好上了一课。

这次反黑让我深刻认识到一个真理:

完全不能相信客户端信息!

但是,又不能完全去除客户端信息,比如通过parameter传递个用户id什么的,要不然怎么确定是那个人的操作呢,呵呵。

其实,我最初的实现是把用户状态放到了session中,但是随着用户量的增加,后台程序被发布到了多台服务器上。

这样拥有状态区分的session用不了了,不得已使用了Memcache,并且通过userId区分把用户信息保存到了MemcacheServer上。

这样只能通过parameter传递用户id,所以就必须对从客户端传递的用户id进行身份验证。

 

下面是两种验证方法

 

1、通过Cookie验证用户

用户登录游戏的时候进行Cookie设置
            String sessionKey = (String) request.getParameter(Constants.PARAM_XN_SIG_SESSION_KEY);
            
            if (sessionKey == null) {
                Exception e = new Exception("xn_sig_session_key is null!");
                throw e;
            }
            
            PremierContext.setCurrentUserSessionKey(sessionKey);
            
            // 把sessionKey保存到Cookie
            Cookie cookie = new Cookie(Constants.COOKIE_KEY_CURRENTSESSIONKEY, sessionKey);
            cookie.setMaxAge(Constants.SECONDS_OF_DAY);
            response.addCookie(cookie);
            // 设置Cookie初始化flag为true,为了初始化Cookie的时候不进行Cookie校验
            request.setAttribute(Constants.REQUEST_KEY_INITCOOKIE, true);
 
通过Cookie进行当前用户校验
        // 从Memcache取得sessionKey
        String sessionKey = (String) MemcacheUtil.get(Constants.MEM_KEY_XN_SIG_SESSION_PREFIX + playerId);
        
        // 通过Cookie进行当前用户校验
        boolean hasCookie = false;
        if (request.getAttribute(Constants.REQUEST_KEY_INITCOOKIE) == null
                || !(Boolean) request.getAttribute(Constants.REQUEST_KEY_INITCOOKIE)) {
            Cookie[] cookies = request.getCookies();
            if (cookies != null && cookies.length != 0) {
                for (Cookie cookie : cookies) {
                    if (StringUtils.equals(Constants.COOKIE_KEY_CURRENTSESSIONKEY, cookie.getName())) {
                        hasCookie = true;
                        if (StringUtils.equals(sessionKey, cookie.getValue())) {
                            // 是同一个用户,验证通过
                            break;
                        } else {
                            Exception e = new Exception("playerId is not the Login player!");
                            throw e;
                        }
                    }
                }
            }
            // 如果没有Cookie,说明用户没有正常登录
            if (!hasCookie) {
                Exception e = new Exception("Cookie is not exit!");
                throw e;
            }
        }

 

2、通过MD5进行签名验证

每个链接传递的参数不止包括用户信息还包括后台生成的签名,获得签名后验证是否合法

            String sigReceived = request.getParameter("sig");

            String appId = request.getParameter("appId");

            String userId = request.getParameter("userId");

            String inviteId = request.getParameter("inviteId");

            ArrayList<String> sigList = new ArrayList<String>();
            sigList.add("appId=" + appId);
            sigList.add("userId=" + userId);
            sigList.add("inviteId=" + inviteId);

            String sigGenerated = CommonUtil.generateSignature(sigList, Constants.SECRET);

            // 如果key相等 或者 算出的sig和收到的sig相等,则校验通过
            if (sigGenerated.equals(sigReceived)) {
...
    /**
     * 生成签名
     * 
     * @param params
     * @param secret
     * @return
     */
    public static String generateSignature(List<String> params, String secret) {

        StringBuffer buffer = new StringBuffer();
        // 对 List<String> params 三个参数字段 采取稳定的字典排序, 然后 append
        Collections.sort(params);
        for (String param : params) {
            buffer.append(param);
        }
        // 继续append 应用的 secret
        buffer.append(secret);
        // 生成 buffer 的 MD5 值
        try {
            java.security.MessageDigest md = java.security.MessageDigest.getInstance("MD5");
            StringBuffer result = new StringBuffer();
            try {
                for (byte b : md.digest(buffer.toString().getBytes("UTF-8"))) {
                    result.append(Integer.toHexString((b & 0xf0) >>> 4));
                    result.append(Integer.toHexString(b & 0x0f));
                }
            } catch (UnsupportedEncodingException e) {
                for (byte b : md.digest(buffer.toString().getBytes())) {
                    result.append(Integer.toHexString((b & 0xf0) >>> 4));
                    result.append(Integer.toHexString(b & 0x0f));
                }
            }
            return result.toString();
        } catch (java.security.NoSuchAlgorithmException ex) {
            logger.error("MD5 does not appear to be supported", ex);
            return Constants.REMARK_EMPTY;
        } catch (Exception e) {
            logger.error("generateSignature error", e);
            return Constants.REMARK_EMPTY;
        }
    }
posted @ 2010-07-23 17:11  mbear  阅读(2324)  评论(0编辑  收藏  举报