spring boot 集成 Oracle Access Manager(OAM)單點登陸

1.介紹

Oracle Access Manager(OAM)是oracle公司開發的身份認證和資源管理解決方案。結合WebGateOHS可實現系統間單點登陸集成。html

oracle中間件產品能夠很是方便的與OAM進行集成,經過配置weblogic安全域(Security Realms),應用幾乎不用作任何改動便可實現單點登陸。java

後臺接口開發中獲取當前登陸用戶是不可避免的一個步驟,spring boot也是如此,本文介紹spring boot與OAM集成的一個可行而且認爲是簡單的方案。git

2.實現

2.1 問題

要實現二者之間的集成須要解決兩個問題:github

  1. spring boot是無狀態應用,如何獲取當前用戶信息
  2. spring boot不是部署weblogic上,沒法經過weblogic 安全域進行單點登陸集成(固然spring boot也能夠部署到weglogic上,這個不是本文討論的方向)

2.2 思路

  1. 經過ohs代理,spring boot能夠獲取cookie等信息
  2. 調用OAM SDK經過cookie獲取當前用戶信息

只要實現以上兩點就能實現spring boot 在脫離weblogic的狀況下也能獲取用戶信息,若是可行,該方案適用於任何後端程序。web

2.3 準備工做

2.3.1 SDK下載安裝

這裏能夠下載sdk,下載時請選擇對應的OAM版本,本文使用11.1.1.5.0版本。spring

爲了使用方便,能夠將jar包安裝到本地maven倉庫segmentfault

mvn install:install-file -Dfile="/Users/asan/workspace/mpaas/lib/oamasdk-api.jar" -DgroupId=com.oracle -DartifactId=oamasdk-api -Dversion=11.1.1.5.0 -Dpackaging=jar

這樣能夠經過如下方式在pom中引用後端

<dependency>
    <groupId>com.oracle</groupId>
    <artifactId>oamasdk-api</artifactId>
    <version>11.1.1.5.0</version>
</dependency>

2.3.2 配置webgate

登陸oamconsole(通常是http://host:7001/oamconsole),在歡迎界面建立一個新的OAM 11g Webgate輸入NameHost Identifier,這兩個默認同樣,必定要將Access Client Password設置爲空,由於在實踐中,sdk沒法解析密碼,報錯信息以下api

2018-10-10 22:46:18 oracle.security.am.asdk.AccessClient initialize
嚴重: Oracle Access SDK 初始化失敗。
oracle.security.am.asdk.AccessException: OAMAGENT-02072: 沒法執行encrypt password操做。
    at oracle.security.am.asdk.impl.Configuration.setEncryptedPassword(Configuration.java:321)
    at oracle.security.am.asdk.impl.ConfigXMLHandler.processConfig(ConfigXMLHandler.java:580)
    at oracle.security.am.asdk.impl.ConfigXMLHandler.readConfigurationFromFile(ConfigXMLHandler.java:126)
    at oracle.security.am.asdk.AccessClient.initialize(AccessClient.java:634)
    at oracle.security.am.asdk.AccessClient.<init>(AccessClient.java:553)
    at oracle.security.am.asdk.AccessClient.createDefaultInstance(AccessClient.java:242)
...

官方說是sdk版本問題,但筆者試過全部的sdk版本仍是沒法解決。瀏覽器

spring-boot-oam-01.png

spring-boot-oam-01.png

點擊右上角Apply後能夠有系統提示,在提示路徑下能夠找到ObAccessClient.xml文件,這個就是webgate的配置文件,在實際環境配置中,須要將該文件放到$OHS_HOME/config/webgate/config目錄下,才能實現ohs資源攔截認證。
ObAccessClient.xml文件複製到本地,如放到目錄/you/path/config/ObAccessClient.xml下。
spring-boot-oam-01.png

2.3.3 代碼實現

直接上代碼

import oracle.security.am.asdk.AccessClient;
import oracle.security.am.asdk.AccessException;
import oracle.security.am.asdk.UserSession;

public class OAM {
    //保存ObAccessClient.xml文件目錄
    public static final String OAM_CONFIG_PATH = "/you/path/config/";

    public static void main(String[] args) throws AccessException {
        String cookie =
            "Ad/NbCxwjXyRvO15xxJiueMY/utzybeifo6bIhhKOcsvQqQM73fIoE/fKm7zdUeo7FqbDYhP6ncFC/Ntq4yeSAXQeIum9EcaCqAXrUVyvO99ACxDv/2OdX5D/R10a7nuc6nlE54zPVbpm5xkp5H8TjlaM0oeqjDZXOalLcAsV9RnQjLrd9rW3cQi05PVuPalf0jRj+pqCILCHf+sO+cCdcYE2jEybbX3oXVwVGYJQSDDIEm/ne56gZlBKOg56zne2zEp60TkyXgkBG8vL0mNqVGdxd9DmNDeGaAYDoif5EzKfXp7K9QtWbGOjwRSGcTZcLvWcBPJjBhTOBGCLJ3yEg==";
        AccessClient client = null;
        client =
                AccessClient.createDefaultInstance(OAM_CONFIG_PATH, AccessClient.CompatibilityMode.OAM_10G);
        UserSession session = new UserSession(cookie);
        String userName = session.getUserIdentity();
        if (userName != null) {
            userName =
                    userName.substring(userName.indexOf("uid=") + 4, userName.indexOf(","));
        }
        System.out.println("current user==>" + userName);
    }
}

cookie能夠在瀏覽器上登陸系統,F12進入開發者面板,在網絡請求裏找到cookie,OAM cookie名稱爲OAMAuthnCookie

3. spring boot實現

上面代碼只是一個demo,跟spring boot也沒什麼關係,在開發中,咱們通常都有個最佳實踐,下面介紹一個我認爲是較佳的實踐方案。在開始以前,建議你閱讀下這篇文章,瞭解一些規範,固然,你也能夠跳過,這個不是核心內容。

在demo裏有個稍微比較複雜的操做,就是從服務器下載ObAccessClient.xml文件,而且保存到本地路徑,在代碼裏引用,並且查看ObAccessClient.xml文件能夠知道里面的內容大部分能夠寫死,只有在建立webgate那個界面填的內容是動態的。所以能夠考慮將配置文件放入到spring boot的application.properties中,而後在程序裏動態生成配置文件,避免引入新的配置文件。

application.properties

mpaas.sso.oam.host=xxxx.example.com
mpaas.sso.oam.port=5575
mpaas.sso.oam.hostId=my_webgate_11g
mpaas.sso.oam.id=my_webgate_11g
mpaas.sso.oam.cookieName=OAMAuthnCookie

建立配置類保存配置信息

OAMConfig.java

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

/**
 * @Copyright: Shanghai Definesys Company.All rights reserved.
 * @Description:
 * @author: jianfeng.zheng
 * @since: 2018/10/10 下午5:45
 * @history: 1.2018/10/10 created by jianfeng.zheng
 */
@Component
@ConfigurationProperties(prefix = "mpaas.sso.oam")
public class OAMConfig {
    private String host;     //OAM服務器地址
    private Integer port;    //OAM服務端口(默認是5575)
    private String name;     //webgate名稱
    private String hostId;   //webgate惟一標識符
    private String cookieName;  //cookie名稱 默認OAMAuthnCookie
    //getter and setter method
    ....
}

主程序

OAMSsoPlugin.java

import com.definesys.mpaas.common.adapter.IMpaasSSOAuthentication;
import com.definesys.mpaas.common.adapter.UserProfile;
import com.definesys.mpaas.common.exception.MpaasBusinessException;
import com.definesys.mpaas.common.exception.MpaasRuntimeException;
import oracle.security.am.asdk.AccessClient;
import oracle.security.am.asdk.AccessException;
import oracle.security.am.asdk.UserSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.FileOutputStream;
import java.util.Map;

/**
 * @Copyright: Shanghai Definesys Company.All rights reserved.
 * @Description:
 * @author: jianfeng.zheng
 * @since: 2018/10/10 下午2:46
 * @history: 1.2018/10/10 created by jianfeng.zheng
 */
@Component
public class OAMSsoPlugin implements IMpaasSSOAuthentication {
    @Autowired
    private OAMConfig config;
    private static AccessClient accessClient;

    //ObAccessClient.xml模板
    public static final String OB_ACCESS_CLIENT_TEMPLATE = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?><CompoundList xmlns=\"http://www.oblix.com\"><SimpleList><NameValPair ParamName=\"id\" Value=\"%s\"/></SimpleList><SimpleList><NameValPair ParamName=\"debug\" Value=\"false\"/></SimpleList><SimpleList><NameValPair ParamName=\"security\" Value=\"open\"/></SimpleList><SimpleList><NameValPair ParamName=\"state\" Value=\"Enabled\"/></SimpleList><SimpleList><NameValPair ParamName=\"primaryCookieDomain\" Value=\"\"/></SimpleList><SimpleList><NameValPair ParamName=\"preferredHost\" Value=\"%s\"/></SimpleList><SimpleList><NameValPair ParamName=\"maxCacheElems\" Value=\"100000\"/></SimpleList><SimpleList><NameValPair ParamName=\"cacheTimeout\" Value=\"1800\"/></SimpleList><SimpleList><NameValPair ParamName=\"cookieSessionTime\" Value=\"0\"/></SimpleList><SimpleList><NameValPair ParamName=\"maxSessionTime\" Value=\"36000\"/></SimpleList><SimpleList><NameValPair ParamName=\"maxConnections\" Value=\"1\"/></SimpleList><SimpleList><NameValPair ParamName=\"idleSessionTimeout\" Value=\"0\"/></SimpleList><SimpleList><NameValPair ParamName=\"failoverThreshold\" Value=\"1\"/></SimpleList><SimpleList><NameValPair ParamName=\"aaaTimeoutThreshold\" Value=\"-1\"/></SimpleList><SimpleList><NameValPair ParamName=\"sleepFor\" Value=\"60\"/></SimpleList><SimpleList><NameValPair ParamName=\"denyOnNotProtected\" Value=\"true\"/></SimpleList><SimpleList><NameValPair ParamName=\"cachePragmaHeader\" Value=\"no-cache\"/></SimpleList><SimpleList><NameValPair ParamName=\"cacheControlHeader\" Value=\"no-cache\"/></SimpleList><SimpleList><NameValPair ParamName=\"ipValidation\" Value=\"false\"/></SimpleList><SimpleList><NameValPair ParamName=\"accessClientPasswd\" Value=\"\"/></SimpleList><ValList ListName=\"primary_server_list\"><ValListMember Value=\"primaryServer1\"/></ValList><ValNameList ListName=\"primaryServer1\"><NameValPair ParamName=\"host\" Value=\"%s\"/><NameValPair ParamName=\"port\" Value=\"%s\"/><NameValPair ParamName=\"numOfConnections\" Value=\"1\"/></ValNameList><ValList ListName=\"proxySSLHeaderVar\"><ValListMember Value=\"IS_SSL\"/></ValList><ValList ListName=\"URLInUTF8Format\"><ValListMember Value=\"true\"/></ValList><ValList ListName=\"client_request_retry_attempts\"><ValListMember Value=\"1\"/></ValList><ValList ListName=\"inactiveReconfigPeriod\"><ValListMember Value=\"10\"/></ValList></CompoundList>";

    /**
     * oam驗證
     *
     * @param header
     * @param cookies
     * @return
     * @throws MpaasBusinessException
     */
    @Override
    public UserProfile ssoAuth(Map<String, String> header, Map<String, String> cookies) throws MpaasBusinessException {
        String path = configPath();
        //AccessClient只要初始化一次就行
        if (accessClient == null) {
            try {
                accessClient =
                        AccessClient.createDefaultInstance(path, AccessClient.CompatibilityMode.OAM_10G);
            } catch (AccessException e) {
                throw new MpaasRuntimeException(e);
            }
        }
        String cookie = parseCookie(cookies);
        String name =
                getUserNameFromToken(cookie);
        UserProfile user = new UserProfile();
        user.setUid(name);
        user.setToken(cookie);
        return user;
    }

    /**
     * 生成配置文件
     *
     * @return
     */
    private String configPath() {
        String tmp = System.getProperty("java.io.tmpdir");
        String path = tmp + "/mpaas-sso-oam-config";
        File dir = new File(path);
        if (!dir.exists()) {
            dir.mkdirs();
        }
        File ob = new File(dir + "/ObAccessClient.xml");
        String xml = null;
        if (!ob.exists()) {
            xml = String.format(OB_ACCESS_CLIENT_TEMPLATE, config.getHostId(), config.getName(), config.getHost(), String.valueOf(config.getPort()));
            FileOutputStream fout = null;
            try {
                fout = new FileOutputStream(ob);
                fout.write(xml.getBytes("utf-8"));
                fout.close();
            } catch (Exception e) {
                throw new MpaasRuntimeException(e);
            }
        }
        return path;
    }

    /**
     * 從cookie中獲取用戶名(uid)
     *
     * @param sessionToken
     * @return
     */
    public static String getUserNameFromToken(String sessionToken) {
        UserSession session = null;
        String userName = null;
        try {
            session = new UserSession(sessionToken);
            userName = session.getUserIdentity();
        } catch (AccessException e) {
            throw new MpaasRuntimeException(e);
        }

        if (userName != null) {
            userName =
                    userName.substring(userName.indexOf("uid=") + 4, userName.indexOf(","));
        }
        return userName;
    }

    /**
     * 解析cookie
     * 須要設置mpaas.sso.oam.cookieName
     * 若是沒有設置默認獲取包含OAMAUTHNCOOKIE的cookie
     *
     * @param cookies
     * @return
     */
    private String parseCookie(Map<String, String> cookies) {
        if (config.getCookieName() != null) {
            return cookies.get(config.getCookieName());
        }
        for (String k : cookies.keySet()) {
            if (k.toUpperCase().contains("OAMAUTHNCOOKIE")) {
                return cookies.get(k);
            }
        }
        throw new MpaasRuntimeException("no config cookie name");
    }
}

測試類
TestController.java

import com.definesys.mpaas.common.adapter.IMpaasSSOAuthentication;
import com.definesys.mpaas.common.adapter.UserProfile;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;

/**
 * @Copyright: Shanghai Definesys Company.All rights reserved.
 * @Description:
 * @author: jianfeng.zheng
 * @since: 2018/10/10 下午7:23
 * @history: 1.2018/10/10 created by jianfeng.zheng
 */
@RestController
public class TestController {

    @Autowired
    private IMpaasSSOAuthentication auth;

    @RequestMapping(value = "/test")
    public UserProfile test(HttpServletRequest request, HttpServletResponse response) {
        Cookie[] cookies = request.getCookies();
        if (cookies == null) {
            return null;
        }
        Map<String, String> ck = new HashMap<>();
        for (Cookie k : cookies) {
            ck.put(k.getName(), k.getValue());
        }
        return auth.ssoAuth(null, ck);
    }
}

代碼能夠在github上下載查看

配置ohs

須要配置ohs反向代理,在ohs配置文件mod_wl_ohs.conf最後添加如下配置項,並重啓ohs

...

ProxyPass /test http://ip:8080/test

ip指向運行spring boot的機器ip

經過單點登陸地址訪問,會先跳轉到登陸頁面,登陸完後可訪問接口而且返回當前用戶信息

寫在最後

更好的作法是在filter中攔截請求作登陸認證,未經過認證的返回401經過認證的將用戶信息保存在ThreadLocal裏,這樣能夠在任意位置獲取用戶信息。這個就留給讀者自行實現。

相關文章
相關標籤/搜索