IDM trust Keycloak

 


前言

由于项目原因,需要启用Keycloak做SSO,但是项目中又使用IDM管制访问权限,我使用的方法如下。


 

一、实现原理

1.用户登录时,先通过Keycloak的登录页;
2.登录通过后,拿Keycloak token换取IDM的信任,并获得IDM token。

 

二、使用步骤

1.Enable Keycloak SSO

1.1 Reference keycloak.js library 

<script src="keycloak.js"></script>

1.2 Add keycloak.init method    

    var initOptions = {
        // responseMode: 'fragment',
        // flow: 'standard'
		url: 'https://keycloak-prd.xxx.com/auth',
        realm: 'xxx',
        clientId: 'xxx'
    };

    var keycloak = Keycloak(initOptions);
	
	keycloak.init({onLoad: 'login-required'}).success(function (authenticated) {
        //alert(authenticated ? 'authenticated' : 'not authenticated');
        if (!authenticated) {
          alert('not authenticated');
        } else {

          keycloak.loadUserProfile().success(data => {
            console.info(data);
          })
        }
        console.info(keycloak);
      }).error(function () {
        alert('failed to initialize');
      });

    function loadProfile() {
        keycloak.loadUserProfile().success(function(profile) {
            output(profile);
        }).error(function() {
            output('Failed to load profile');
        });
    }

1.3 After the above settings, the system will be redirected to your keycloak's login page. 

 

2.Trust keycloak's token in IDM

代码如下(示例):

        /// <summary>
        /// Login with Keycloak
        /// </summary>
        [HttpGet]
        public async Task<IActionResult> LoginWithKeycloak(string returnUrl, string keyCloakToken)
        {
            if (string.IsNullOrEmpty(returnUrl))
            {
                return Redirect("~/");
            }

            if (string.IsNullOrEmpty(keyCloakToken))
            {
                returnUrl = returnUrl + (returnUrl.IndexOf('?') > 0 ? "&" : "?") + "errmsg=No Keycloak token";
                return Redirect(returnUrl);
            }
            bool logged = false;
            string errmsg = string.Empty;
            try
            {
                var jsonPayload = Base64UrlEncoder.Decode(keyCloakToken.Split('.')[1]);
                JObject claims = (JObject)JsonConvert.DeserializeObject(jsonPayload);
                string keyCloakUrl = claims["iss"].ToString() + "/account";

                //从工厂获取请求对象
                var client = _httpClientFactory.CreateClient();
                var request = new HttpRequestMessage()
                {
                    Method = HttpMethod.Get,
                    RequestUri = new Uri(keyCloakUrl),
                    Content = new StringContent(string.Empty)
                };
                request.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
                request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", keyCloakToken);
                var response = await client.SendAsync(request);

                if (response.IsSuccessStatusCode)
                {
                    var responseContent = response.Content.ReadAsStringAsync().Result;
                    JObject user = (JObject)JsonConvert.DeserializeObject(responseContent);

                    /*{ "username": "", "firstName": "",  "lastName": "", "email": "", "emailVerified": false, "attributes": { "LDAP_ENTRY_DN": ["" ], "modifyTimestamp": [""], "createTimestamp": [""], "LDAP_ID": [""] }}*/
                    AuthenticationProperties props = null;
                    // issue authentication cookie with subject ID and username
                    var isuser = new IdentityServerUser(user["username"].ToString().ToUpper())
                    {
                        DisplayName = user["firstName"].ToString()
                    };
                    await HttpContext.SignInAsync(isuser, props);
                    logged = true;
                }
            }
            catch (Exception ex)
            {
                errmsg = ex.Message;
            }

            returnUrl = returnUrl + (returnUrl.IndexOf('?') > 0 ? "&" : "?") + ("step=IdmLogin") + (logged ? "" : ("&errmsg=" + errmsg));
            return Redirect(returnUrl);
        }

 

3.Get IDM's token

代码如下(示例):

index2.html 

<html>
<head>
    <script src="keycloak.js"></script>	
	<script src="oidc-client.min.js"></script>

</head>
<body>

<div>
    <button onclick="keycloak.login()">Login</button>
    <button onclick="keycloak.login({ action: 'UPDATE_PASSWORD' })">Update Password</button>
    <button onclick="keycloak.logout()">Logout</button>
    <button onclick="keycloak.register()">Register</button>
    <button onclick="keycloak.accountManagement()">Account</button>
    <button onclick="refreshToken(9999)">Refresh Token</button>
    <button onclick="refreshToken(30)">Refresh Token (if <30s validity)</button>
    <button onclick="loadProfile()">Get Profile</button>
    <button onclick="updateProfile()">Update profile</button>
    <button onclick="loadUserInfo()">Get User Info</button>
    <button onclick="output(keycloak.tokenParsed)">Show Token</button>
    <button onclick="output(keycloak.refreshTokenParsed)">Show Refresh Token</button>
    <button onclick="output(keycloak.idTokenParsed)">Show ID Token</button>
    <button onclick="showExpires()">Show Expires</button>
    <button onclick="output(keycloak)">Show Details</button>
    <button onclick="output(keycloak.createLoginUrl())">Show Login URL</button>
    <button onclick="output(keycloak.createLogoutUrl())">Show Logout URL</button>
    <button onclick="output(keycloak.createRegisterUrl())">Show Register URL</button>
    <button onclick="output(keycloak.createAccountUrl())">Show Account URL</button>

</div>

<h5>Result of Keycloak</h5>
<pre style="background-color: #ddd; border: 1px solid #ccc; padding: 10px; word-wrap: break-word; height:100px; overflow: scroll;" id="output"></pre>

<h5>Events of Keycloak</h5>
<pre style="background-color: #ddd; border: 1px solid #ccc; padding: 10px; word-wrap: break-word; height:50px; overflow: scroll;" id="events"></pre>


<div>
    <button onclick="renewIdmToken()">Refresh Token</button>
</div>

<h5>Result of IDM</h5>
<pre style="background-color: #ddd; border: 1px solid #ccc; padding: 10px; word-wrap: break-word; height:100px; overflow: scroll;" id="outputidm"></pre>

<h5>Events of IDM</h5>
<pre style="background-color: #ddd; border: 1px solid #ccc; padding: 10px; word-wrap: break-word; height:50px; overflow: scroll;" id="eventsidm"></pre>


<script>

</script>
<script src="idm-config.js"></script>
<script src="keycloak-config.js"></script>

</body>
</html>

idm-config.js

​

	let initIdmOptions = {
		authority: "https://xx.xxx.com/auth", 
		client_id: "peoplex", 
		redirect_uri: "https://xx.xxx.com/keycloak/idm-callback.html",
		response_type: "id_token token",
		scope: "openid profile api1",
		post_logout_redirect_uri: "https://xx.xxx.com/keycloak/index2.html",

		// silent renew will get a new access_token via an iframe 
		// just prior to the old access_token expiring (60 seconds prior)
		silent_redirect_uri: "https://xx.xxx.com/keycloak/idm-silent.html",
		automaticSilentRenew: true,
	 }
  
	var oidcMgr = new Oidc.UserManager(initIdmOptions);
	
	
	function loginWithIdentityServer4(token)
	{
		var url = initIdmOptions.authority + '/Account/LoginWithKeycloak?returnUrl={0}&keyCloakToken={1}';
		url = url.replace('{0}',window.location.href).replace('{1}',token);
		window.location.replace(url);
	}
	
	function idmLogin() {
		oidcMgr.signinRedirect();
	}
	
	function idmLogout() {
		oidcMgr.signoutRedirect();
	}
	
	function idmRedirectCallback()
	{
		new Oidc.UserManager().signinRedirectCallback().then(function (user) {
			console.log(user);
			window.history.replaceState({},
				window.document.title,
				window.location.origin + window.location.pathname);
			window.location = "index2.html?step=Completed";
		});
	}
	
	function IdmSilentCallback()
	{
		oidcMgr.signinSilentCallback();
	}
	
	function getIdmToken() {
		oidcMgr.getUser().then(function (user) {
			if (user) {
				console.log(user);
				outputIdm(user);
			} else {
				console.log("Not logged in");
				outputIdm("Not logged in");
			}
		});
	}

	function renewIdmToken() {
	  oidcMgr.signinSilent()
		.then(function () {
		  console.log("silent renew success");
		}).catch(function (err) {
		  console.log("silent renew error", err);
		});
	}
	
	function outputIdm(data) {
        if (typeof data === 'object') {
            data = JSON.stringify(data, null, '  ');
        }
        document.getElementById('outputidm').innerHTML = data;
    }

    function eventIdm(event) {
        var e = document.getElementById('eventsidm').innerHTML;
        document.getElementById('eventsidm').innerHTML = new Date().toLocaleString() + "\t" + event + "\n" + e;
    }

	oidcMgr.events.addUserLoaded(function (user) {
	  //console.log("User loaded");
	  eventIdm("User loaded");
	  getIdmToken();
	});
	oidcMgr.events.addUserUnloaded(function () {
	  //console.log("User logged out locally");
	  eventIdm("User logged out locally");
	  getIdmToken();
	});
	oidcMgr.events.addAccessTokenExpiring(function () {
	  //console.log("Access token expiring..." + new Date());
	  eventIdm("Access token expiring...");
	});
	oidcMgr.events.addSilentRenewError(function (err) {
	  //console.log("Silent renew error: " + err.message);
	  eventIdm("Silent renew error: " + err.message);
	});
	oidcMgr.events.addUserSignedOut(function () {
	  //console.log("User signed out of OP");
	  eventIdm("User signed out of OP");
	  oidcMgr.removeUser();
	});

​

idm-callback.html 

<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8" />
  <title></title>
  <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <meta http-equiv="Expires" content="0">
  <meta http-equiv="Pragma" content="no-cache">
  <meta http-equiv="Cache-control" content="no-cache">
  <meta http-equiv="Cache" content="no-cache">
  <script type="text/javascript" src="oidc-client.min.js"></script>
  
</head>

<body>
  Loading...
  
  <script src="idm-config.js"></script>
  <script>
	idmRedirectCallback();
  </script>
  
</body>

</html>

idm-silent.html 

<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8" />
  <title></title>
  <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <meta http-equiv="Expires" content="0">
  <meta http-equiv="Pragma" content="no-cache">
  <meta http-equiv="Cache-control" content="no-cache">
  <meta http-equiv="Cache" content="no-cache">
  <script type="text/javascript" src="oidc-client.min.js"></script>

</head>

<body>
  Loading...
  <script src="idm-config.js"></script>
  <script>
	IdmSilentCallback();
  </script>
</body>

</html>

keycloak-config.js 

	let initOptions = {
		url: 'https://xx.xxx.com/auth',
        realm: 'k8sprdwzsi40',
        clientId: 'wigps',
		onLoad: 'login-required'
	}

	let keycloak = Keycloak(initOptions);

	keycloak.init({ onLoad: initOptions.onLoad }).then((auth) => {
		if (!auth) {
			alert('not authenticated');
			window.location.reload();
			return;
		} 
		
		let errmsg = getQueryString('errmsg');
		if(errmsg){
			alert(getQueryString('errmsg'));
			return;
		}
		
		let step = getQueryString('step');
		if(!step){
			loginWithIdentityServer4(keycloak.token);
			return;
		}
		else{
			switch (step){
				case 'IdmLogin':
					idmLogin();
					break;
				case 'Completed':
					getIdmToken();
					loadProfile();
					break;	
				default:
					break;
			}	
		}

		//Token Refresh
		setInterval(() => {
			refreshToken(70);
		}, 1000*60)

	}).catch(() => {
		alert("Authenticated Failed, ailed to initialize");
	});
	
	function getQueryString(name) {
        var reg = new RegExp('(^|&)' + name + '=([^&]*)(&|$)', 'i');
        var r = window.location.search.substr(1).match(reg);
        if (r != null) {
            return unescape(r[2]);
        }
        return null;
	}


    function loadProfile() {
        keycloak.loadUserProfile().then(function(profile) {
            output(profile);
			console.log(keycloak);
        }).catch(function() {
            output('Failed to load profile');
        });
    }

    function updateProfile() {
        var url = keycloak.createAccountUrl().split('?')[0];
        var req = new XMLHttpRequest();
        req.open('POST', url, true);
        req.setRequestHeader('Accept', 'application/json');
        req.setRequestHeader('Content-Type', 'application/json');
        req.setRequestHeader('Authorization', 'bearer ' + keycloak.token);

        req.onreadystatechange = function () {
            if (req.readyState == 4) {
                if (req.status == 200) {
                    output('Success');
                } else {
                    output('Failed');
                }
            }
        }

        req.send('{"email":"myemail@foo.bar","firstName":"test","lastName":"bar"}');
    }

    function loadUserInfo() {
        keycloak.loadUserInfo().then(function(userInfo) {
            output(userInfo);
        }).catch(function() {
            output('Failed to load user info');
        });
    }

    function refreshToken(minValidity) {
        keycloak.updateToken(minValidity).then(function(refreshed) {
            if (refreshed) {
                output(keycloak.tokenParsed);
				renewIdmToken();
            } else {
                output('Token not refreshed, valid for ' + Math.round(keycloak.tokenParsed.exp + keycloak.timeSkew - new Date().getTime() / 1000) + ' seconds');
            }
        }).catch(function() {
            output('Failed to refresh token');
        });
    }

    function showExpires() {
        if (!keycloak.tokenParsed) {
            output("Not authenticated");
            return;
        }

        var o = 'Token Expires:\t\t' + new Date((keycloak.tokenParsed.exp + keycloak.timeSkew) * 1000).toLocaleString() + '\n';
        o += 'Token Expires in:\t' + Math.round(keycloak.tokenParsed.exp + keycloak.timeSkew - new Date().getTime() / 1000) + ' seconds\n';

        if (keycloak.refreshTokenParsed) {
            o += 'Refresh Token Expires:\t' + new Date((keycloak.refreshTokenParsed.exp + keycloak.timeSkew) * 1000).toLocaleString() + '\n';
            o += 'Refresh Expires in:\t' + Math.round(keycloak.refreshTokenParsed.exp + keycloak.timeSkew - new Date().getTime() / 1000) + ' seconds';
        }

        output(o);
    }

    function output(data) {
        if (typeof data === 'object') {
            data = JSON.stringify(data, null, '  ');
        }
        document.getElementById('output').innerHTML = data;
    }

    function event(event) {
        var e = document.getElementById('events').innerHTML;
        document.getElementById('events').innerHTML = new Date().toLocaleString() + "\t" + event + "\n" + e;
    }
	

    keycloak.onAuthSuccess = function () {
        event('Auth Success');
    };

    keycloak.onAuthError = function (errorData) {
        event("Auth Error: " + JSON.stringify(errorData) );
    };

    keycloak.onAuthRefreshSuccess = function () {
        event('Auth Refresh Success');
    };

    keycloak.onAuthRefreshError = function () {
        event('Auth Refresh Error');
    };

    keycloak.onAuthLogout = function () {
        event('Auth Logout');
    };

    keycloak.onTokenExpired = function () {
        event('Access token expired.');
    };

    keycloak.onActionUpdate = function (status) {
        switch (status) {
            case 'success':
                event('Action completed successfully'); break;
            case 'cancelled':
                event('Action cancelled by user'); break;
            case 'error':
                event('Action failed'); break;
        }
    };

Please see the getIdmToken() method in the idm-config.js for IDM's token。


总结

以上就是今天要讲的内容,本文仅仅简单介绍了IDM和keycloak的使用,而IDM和keycloak提供了大量函数和方法。

 

    

 

    

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
IDM是指Internet Download Manager,是一款常用的下载管理软件。在Python中,可以使用os.system()或subprocess模块来调用IDM进行下载。 使用os.system()调用IDM进行下载的示例代码如下: ```python import os def IDMdownload(DownUrl, DownPath, FileName): IDM = r"D:\IDM\Internet Download Manager\IDMan.exe" os.chdir(IDMPath) IDM = "IDMan.exe" command = ' '.join(\[IDM, '/d', DownUrl, '/p', DownPath, '/f', FileName, '/q'\]) print(command) os.system(command) ``` 这段代码中,通过os.system()函数调用IDM进行下载,其中`DownUrl`是下载链接,`DownPath`是下载路径,`FileName`是下载文件的名称。使用`/d`参数指定下载链接,`/p`参数指定下载路径,`/f`参数指定下载文件的名称,`/q`参数表示下载成功后IDM将退出。 另一种调用IDM进行下载的方法是使用subprocess模块,示例代码如下: ```python from subprocess import call IDM = r"D:\IDM\Internet Download Manager\IDMan.exe" DownPath = 'D:/下载/' local_file_name = 'xx' urlList = \['xxx'\] for ul in urlList: call(\[IDM, '/d', ul, '/p', DownPath, '/f', local_file_name, '/n', '/a'\]) call(\[IDM, '/s'\]) ``` 这段代码中,通过subprocess模块的call()函数调用IDM进行下载。循环遍历`urlList`中的下载链接,使用`/d`参数指定下载链接,`/p`参数指定下载路径,`/f`参数指定下载文件的名称,`/n`参数表示当IDM不出问题时启动静默模式,`/a`参数表示添加指定文件到下载队列,但不进行下载。最后使用`/s`参数开始下载队列中的任务。 以上是使用Python调用IDM进行下载的示例代码。希望对你有帮助!\[1\]\[2\] #### 引用[.reference_title] - *1* *2* [Python 调用IDM下载器](https://blog.csdn.net/weixin_44072750/article/details/120707399)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [python+IDM实现快速批量化下载 (解决IDM批量化下载文件类型出现Error问题)](https://blog.csdn.net/qq_41985248/article/details/127730107)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值