【仅供内部供应商使用,不提供对外解答和培训】

Page tree

Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

Table of Contents

一、特殊名词介绍

二、背景、场景介绍

在帆软决策平台中默认提供了3种登录认证的方法。分别是 平台内置认证、LDAP认证、HTTP认证。其中HTTP认证主要用于将用户在帆软自身的登录页面输入的用户名密码(包括sso接口)给到第三方进行验证用户密码的合法性。在10.0的决策平台中,产品出于对安全的考虑,增强了该认证方式的后台传输要求。但无形中使得所有9.0和8.0版本已经开发好在使用的所有http认证用户,无法在新版本中直接使用。故在10.0中标准HTTP认证时,允许用户自行通过HttpAuthorizeProvider接口对认证的具体方式进行自定义。

主要场景包括:

1、8.0、9.0的http认证升级到10.0时需要做适配开发

2、用户信息同步时无法同步到密码信息,但可以通过某种算法或某些方法直接获取到用户名密码的合法性(不一定是http请求)

3、客户已有符合10.0安全加密标准的http认证服务,但是返回值不符合帆软决策平台要求的场景。

三、接口介绍

Code Block
languagejava
themeEclipse
firstline1
titleHttpAuthorizeProvider.java
linenumberstrue
package com.fr.decision.fun;

import com.fr.stable.fun.mark.Mutable;

/**
 * http认证处理接口
 */
public interface HttpAuthorizeProvider extends Mutable {

    String MARK_STRING = "HttpAuthorizeProvider";

    int CURRENT_LEVEL = 1;

    /**
     * 是直接替换掉内置的所有处理(包括RSA加密部分),还是仅替换校验返回值
     */
    Scope scope();

    /**
     * 认证过程
     * @param username 登录用户名
     * @param inputPassword 登录用户输入的密码
     * @param savedPassword 决策平台内置的数据库中存储的该用户的密码
     * @param hashPassword 密码的hash值
     * @return 认证成功则返回true,否则返回false
     */
    boolean authorize(String username, String inputPassword, String savedPassword, String hashPassword);

    /**
     * 认证过程
     * @return 认证成功则返回true,否则返回false
     */
    boolean authorize(String uuid, String returnMessage);

    enum Scope {
        /**
         * 表示替换到内置http认证的处理逻辑,自行在authorize方法中处理
         */
        REPLACE,
        /**
         * 表示只是替换内置http认证中,对于返回值的判断处理
         */
        CHECK
    }
}


Code Block
languagejava
themeEclipse
firstline1
titleHttpPassport.java
linenumberstrue
package com.fr.decision.authorize.impl;

...

public class HttpPassport extends AbstractPassport {

    private static final String PARAMETER_NAME = "data";

    ...

    @Override
    public boolean checkTicket(String username, String inputPassword, String savedPassword, String hashPassword) {
        Set<HttpAuthorizeProvider> providers = ExtraDecisionClassManager.getInstance().getArray(HttpAuthorizeProvider.MARK_STRING);
        for (HttpAuthorizeProvider provider : providers) {
            if (HttpAuthorizeProvider.Scope.REPLACE == provider.scope()) {
                boolean result = provider.authorize(username, inputPassword, savedPassword, hashPassword);
                if (result) {
                    return true;
                }
            }
        }
        String publicKey = getDecryptedPublicKey();
        if (StringUtils.isNotBlank(publicKey)) {
            String message;
            String uuid = UUID.randomUUID().toString();
            try {
                HashMap<String, String> para = new HashMap<String, String>();
                Key key = SecurityToolbox.string2PublicKey(publicKey);
                String data = SecurityToolbox.encrypt(
                        requestText(username, inputPassword, savedPassword, hashPassword, uuid),
                        key
                );
                para.put(PARAMETER_NAME, data);
                message = SecurityToolbox.decrypt(HttpToolbox.get(getUrl(), para), key);
            } catch (Exception e) {
                throw new RuntimeException("Http Request Failed:" + e.getMessage());
            }

            boolean result = checkMessage(uuid, message);
            if (result) {
                return true;
            }
            for (HttpAuthorizeProvider provider : providers) {
                if (provider.scope() == HttpAuthorizeProvider.Scope.CHECK) {
                    result = provider.authorize(uuid, message);
                    if (result) {
                        return true;
                    }
                }
            }
        }
        return false;
    }

    private boolean checkMessage(String uuid, String message) {
        if (StringUtils.isEmpty(message)) {
            return false;
        }
        JSONObject data = new JSONObject(message);
        return data.optBoolean(SUCCESS_MARK) && uuid.equals(data.optString(UUID_FIELD));
    }

    private String requestText(String username, String password, String savedPassword, String hashPassword, String uuid) throws Exception {
        Map<String, String> map = new HashMap<String, String>();
        map.put("username", username);
        map.put("password", password);
        map.put("savedPassword", savedPassword);
        map.put("hashPassword", hashPassword);
        map.put(UUID_FIELD, uuid);
        ObjectMapper mapper = new ObjectMapper();
        return mapper.writeValueAsString(map);
    }

    ...
}


四、支持版本

产品线

版本

支持情况

备注

FR10.0支持
BI5.1支持
BI5.1.2支持
BI5.1.3支持

五、插件注册

Code Block
languagexml
themeEclipse
firstline1
titleplugin.xml
linenumberstrue
<extra-decision>
    <HttpAuthorizeProvider class="your class name"/>
</extra-decision>


六、原理说明

HttpPassport 是Passport接口的实例之一,在登录逻辑中被调用。具体调用过程可参见登录专题视频。在登录验证的过程中checkTicket方法执行时会调用所有插件中申明的http认证自定义处理实例。

七、特殊限制说明

接口包含两个authorize接口方法。通过对HttpPassport代码逻辑的了解,可以看出当Scope为REPLACE时,会优先执行authorize(String username, String inputPassword, String savedPassword, String hashPassword)。如果结果为true,那么整个认证就是成功的。后续的方法就不再执行了。也即是在这个方法里面,我们可以拿到用户输入的用户名和密码,自行验证结果,无需受产品自身的加解密限制。也无需发送http请求。此时接口的功能基本等同于PassportProvider接口的功能,有点区别就是HttpAuthorizeProvider#authorize方法为返回false时,还会继续执行本身配置的http认证。如果Scope为CHECK时,authorize(String username, String inputPassword, String savedPassword, String hashPassword)不会执行,而是直接执行产品的http认证,然后仅仅是把返回值通过authorize(String uuid, String returnMessage)方法进行处理。确认认证的结果

注:该认证方式对超管用户无效!

八、常用链接

demo-http-authorize-provider

九、开源案例

免责声明:所有文档中的开源示例,均为开发者自行开发并提供。仅用于参考和学习使用,开发者和官方均无义务对开源案例所涉及的所有成果进行教学和指导。禁止用于任何商业用途,若作为商用一切后果责任由使用者自行承担。

open-JSD-7814

demo-auth-http