NCC系列(三)单点登录

https://community.yonyou.com/article/detail/1274

https://community.yonyou.com/article/detail/207

https://community.yonyou.com/article/detail/165

1、单点登录的流程

2、单点登录的流程简化

客户端只需要发起单点登录的请求,省去获取token,封装单点登录url的过程,NCC服务器直接返回单点登录的url,按照这个思路实现单点登录。

3、NCC表中注册第三方系统信息

​ 在NC Cloud的系统注册表sm_oauth_security注册第三方系统(插入一条数据)

表中各字段含义如下:

  • CLIENT_ID :第三方系统编码,必需项
  • CLIENT_NAME: 第三方系统名称,非必需
  • CLIENT_SECURITY: 第三方系统和NC Cloud共同维护的秘钥对
  • PK_OAUTH_SECURITY: 主键
  • CLIENT_URL: 第三方系统的url (供NC Cloud集成访问第三方系统使用,本环节不需要)
  • CLIENT_AUTHCLASS: 登录第三方系统授权类 (供NC Cloud集成访问第三方系统使用,本环节不需要)
  • PK_GROUP:非本环节使用,设置为空

4、白名单设置

在home/ierp/sf/nccssoConfig.xml中配置白名单,如果无需单独处理权限,可以在第一个authenticator标签的listParam中配置ip白名单

如果需要单独定单点登录权限可以在第二个中处理。一般只需在第一个中配置即可。

<?xml version="1.0" encoding="UTF-8"?>
<NCCSSOConfigVO>
	<authenticator id="default" classname="ncc.sso.bs.DefaultNCCSSOAuthenticator">
		<regTimeOut>200</regTimeOut>
		<listParam key="IPAddress">
			<string>127.0.0.1</string>
			<string>10.11.115.14</string>
		</listParam>
	</authenticator>
	<authenticator id="type" classname="nc.sso.bs.AAA">
		<regTimeOut>200</regTimeOut>
		<listParam key="IPAddress">
			<string>127.0.0.1</string>
		</listParam>
	</authenticator>
</NCCSSOConfigVO>

5、单点登录案例代码

单点登录使用的jar包

NCCloud、基础技术、技术与框架NCC系列(三)单点登录插图
###查看client_id和client_security 
>>>>>select * from sm_oauth_security
import java.io.DataOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.net.URLConnection;
import nccloud.security.impl.SignatureTookKit;
import org.apache.commons.codec.binary.Base64;

public class PostThirdPartyAccessTokenTest {
    public static String BaseUrl = "http://ip:port";

    public static void main(String[] args) throws Exception{
        String dsname = "nccdb";//数据源的编码,本地开发为design
        String usercode = "100134";//用户的编码,需要用户可以正常登陆
        String client_id = "nccapi";//第三方系统id,
        String client_security = "*****c02be45fffe2***********";//第三方秘钥
        String busicentercode = "001";//账套编码,本地开发时为develop
        String url=BaseUrl+"/service/genThirdPartyAccessToken";
        String security = genKey(usercode,usercode + client_security + (System.currentTimeMillis() + "").substring(0, 6));
        String write="type=type_security&dsname="+dsname+"&usercode="+usercode+"&client_id="+client_id+"&security="+security+"&busicentercode="+busicentercode;

        write=write.replaceAll("\\+","%2B");//避免出现特殊符号问题。

        //获取token
        String token = getToken(url,write);
        if("".equals(token)) {
            System.out.println("获取token失败");
            return;
        }


        System.out.println("获取的token为:"+token);
        System.out.println("单点登陆路径为:"+getBaseRedirectUrl(token));
//        System.out.println("审批详情单点登陆路径为:"+getMsgCenterRedirectUrl(token));
//        System.out.println("单点登陆到单据详情:"+getBillDetailRedirectUrl(token));
    }

    /**
     * 单点登录到首页:http://127.0.0.1:8080/nccloud
     * @param token 单点登录获取到的token
     * */
    private static String getBaseRedirectUrl(String token) {
        //单点成功后的跳转路径
        String redirect_uri = BaseUrl+"/nccloud";
        //单点登陆时的全路径,可以直接粘贴到浏览器进行访问。
        String fullUrl=BaseUrl+"/nccloud/resources/uap/rbac/thirdpartylogin/main/index.html?accesstoken="+token+"&redirect_uri="+redirect_uri;
        return fullUrl;
    }

    /**
     * 单点登陆到单据详情,单据节点打开对应的单据
     * 这里只是拼了单点登陆的url,前端节点需要this.props.getUrlParam('id')获取单据id
     * 将id查询出来后对单据进行展示
     * @param token 单点登陆获取到的token
     * */
    private static String getBillDetailRedirectUrl(String token) throws UnsupportedEncodingException {
        String billId="1001A1100000018BIE2E";//单据id--例如本例子的采购订单 select pk_order from po_order where 1=1 and vbillcode='CD2025020800003436'
        String appcode="400400800";//单据应用编码
        String pagecode="400400800_card";//单据页面编码

        StringBuffer redirectUrl = new StringBuffer();
        redirectUrl.append(BaseUrl+"/nccloud/resources/workbench/public/common/main/index.html#/ifr?ifr=");
        redirectUrl.append( URLEncoder.encode(URLEncoder.encode("/nccloud/resources/pu/pu/poorder/main/index.html#/card?id="+billId+"&status=browse&scene=linksce","UTF-8"),"UTF-8"));//这里拼的路径是节点打开后iframe 标签src的路径。
        redirectUrl.append("&b1="+URLEncoder.encode(URLEncoder.encode("供应链","UTF-8"),"UTF-8"));//b1,面包屑导航,首页后第一级
        redirectUrl.append("&b2="+URLEncoder.encode(URLEncoder.encode("采购管理","UTF-8"),"UTF-8"));//b2,面包屑导航,第二级
        redirectUrl.append("&b3="+URLEncoder.encode(URLEncoder.encode("采购订单","UTF-8"),"UTF-8"));//b3,面包屑导航,第三级
        redirectUrl.append("&n="+URLEncoder.encode(URLEncoder.encode("采购订单维护","UTF-8"),"UTF-8"));//n,菜单名字
        redirectUrl.append("&c="+appcode);//应用编码
        redirectUrl.append("&p="+pagecode);//页面编码
        redirectUrl.append("&ar=0001Z81000000002UHUZ");//应用注册对应的小应用主键,这里示例代码用的客户申请单
        String fullUrl =BaseUrl+"/nccloud/resources/uap/rbac/thirdpartylogin/main/index.html?accesstoken="+token+"&redirect_uri="+redirectUrl.toString();
        return fullUrl;
    }

    /**调用接口获取token
     *
     * @param url 要调用的接口
     * @param write 需要写出的数据
     * @return token值
     * */
    private static String getToken(String url,String write) {


        OutputStream outStream=null;
        DataOutputStream dataOutput=null;
        InputStream inStream  = null;
        String ret="";
        try {
            URL preUrl = new URL(url);
            URLConnection con = preUrl.openConnection();

            //设置为true,后面可以调用getOutputStream并传输数据
            con.setDoOutput(true);
            //不使用http缓存
            con.setUseCaches(false);
            //设置http请求头
            con.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
            con.setRequestProperty("Content-Length", "10000" );

            HttpURLConnection httpCon = (HttpURLConnection) con;
            //这里使用post方式调用接口
            httpCon.setRequestMethod("POST");
            //获取输出流
            outStream = httpCon.getOutputStream();
            dataOutput = new DataOutputStream(outStream);
            //接口需要的参数作为字节序列写入输出流
            dataOutput.writeBytes(write);
            dataOutput.flush();
            //获取输入流
            inStream  = httpCon.getInputStream();
            //读取服务返回的数据
            int ch;
            while ((ch = inStream.read()) != -1) {
                ret += String.valueOf((char) ch);
            }

        } catch (Exception e) {
            System.out.println(e);
        } finally {
            //关闭服务输入流和输出流
            if (dataOutput != null) {
                try {
                    dataOutput.close();
                } catch (Exception e2) {
                    e2.printStackTrace();
                }
            }
            if (outStream != null) {
                try {
                    outStream.close();
                } catch (Exception e2) {
                    e2.printStackTrace();
                }
            }
            if (inStream != null) {
                try {
                    inStream.close();
                } catch (Exception e2) {
                    e2.printStackTrace();
                }
            }
        }
        //返回读取的token
        return ret;
    }


    private static String genKey(String userid,String key) throws Exception{
        return new Base64().encodeToString(SignatureTookKit.digestSign(userid.getBytes(), key.getBytes()));
    }
}

在单点登录跳转到单据详情页面的时候,应该赋什么值到对应的b1、b2、b3、n、c、p等参数呢?

NCCloud、基础技术、技术与框架NCC系列(三)单点登录插图1
NCCloud、基础技术、技术与框架NCC系列(三)单点登录插图2

**记得两次解码,因为跳转拼接的时候是两次urlencode**