目录
1.使用github开源的的库 keycloak-angular:
安装Keycloak Angular和keycloak客户端keycloak-js库:
2.自己实现集成Keycloak功能 -- 实现Oauth2.0流程 :
前言:
一个企业往往拥有多个应用系统,如果每个应用都有独立的用户认证和授权管理功能,这不仅需要运维人员维护多套用户管理系统,用户使用每个系统时都要登录一次,非常不方便。如果能够将所有应用系统的用户集中管理,用户只登录一次,就可以无需再次登录而访问所有系统,这将会大大改善用户体验。本文前端Angular, 后端Django,基于Keycloak搭建单点登录系统。所以代码均经过验证,直接从工程代码中拷贝。
前端代码部署到nginx, 前后端代码分开部署
前端Angular集成Keycloak:
本文使用前端登录,而不是后端登录的方式。后端只负责检测前端发送的请求携带的access token是否可以访问Keycloak,以及访问Keycloak server的几个API。
前端要怎样集成Keycloak呢? 本文提供两种方式
1.使用github开源的的库 keycloak-angular:
原始的keycloak js adapter:前端集成Keycloak,根据官方文档,keycloak提供了一个JavaScript adapter,详情请参考:https://www.keycloak.org/docs/latest/securing_apps/index.html#_javascript_adapter
这个keycloak-angular库对keycloak提供的这个JavaScript adapter进行了包装,可以帮助用户在Angular应用中更方便的使用, 请参考github:GitHub - mauriciovigolo/keycloak-angular: Easy Keycloak setup for Angular applications. 它提供了以下特性:
- 提供了一个keycloak服务,它包装了要在Angular中使用的keycloak-js函数,为原始函数(keycloak-js文件中提供的函数)提供了额外的功能,并且还添加了新的函数,使其更容易被Angular应用程序使用
- 通用的AuthGuard实现,用户可以通过继承其认证逻辑和角色加载,自定义所需要的AuthGuard逻辑。
- 提供了一个HttpClient拦截器,登录成功之后,每个http请求都会在header中增加认证信息(其实就是登录成功之后的keycloak access token),用户可以禁用这个拦截器,也可以设置排除某个url的http请求。下面是拦截器源码中,给http请求的header中增加access token的代码:
/**
* Adds the token of the current user to the Authorization header
*
* @param req
* @param next
*/
private handleRequestWithTokenHeader(
req: HttpRequest<any>,
next: HttpHandler
): Observable<any> {
return this.keycloak.addTokenToHeader(req.headers).pipe(
mergeMap(headersWithBearer => {
const kcReq = req.clone({ headers: headersWithBearer });
return next.handle(kcReq);
})
);
}
安装Keycloak Angular和keycloak客户端keycloak-js库:
具体版本请根据自己的angular版本和keycloak版本进行设置。
npm install keycloak-angular keycloak-js
本文所用版本:
当然,如果用户想用自己keycloak服务器中的keycloak-js,可以选择不安装keycloak-js库,而是在index.html入口文件中直接下载keycloak server中的keycloak.js文件。
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Conduit</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="//code.ionicframework.com/ionicons/2.0.1/css/ionicons.min.css" rel="stylesheet" type="text/css">
<link href="//fonts.googleapis.com/css?family=Titillium+Web:700|Source+Serif+Pro:400,700|Merriweather+Sans:400,700|Source+Sans+Pro:400,300,600,700,300italic,400italic,600italic,700italic" rel="stylesheet" type="text/css">
<link rel="stylesheet" href="//demo.productionready.io/main.css">
<!-- 导入keycloak服务器的keycloak.js脚本文件 -->
<script src="http://127.0.0.1:8082/auth/js/keycloak.js" type="text/javascript"></script>
</head>
Angular中设置Keycloak:
为了确保Keycloak在Angular应用启动时被初始化,必须在AppModule中添加一个APP_INITIALIZER provider。这个provider将调用如下所示的initializeKeycloak工厂函数,该函数将设置Keycloak服务,以便在应用程序中使用它。
keycloak-init.ts
Note0: 这里我们把client_secret( 下面代码中的credentials: )的值设置到了前端,用于在登录成功后,前端利用返回的授权码code和我们这里设置的client_secret直接访问keycloak API去获取access token;而不是将client_secret保存到后端,前端发送http请求,将授权码code发送到后端,后端利用授权码code和已经保存配置好的client_secret去调用keycloak的API去获取access token,并返回给前端,第2章节就是用的这种方式,哪种方式更安全呢? 我认为第2章节的方式更安全。
import { KeycloakService } from 'keycloak-angular';
export function initializeKeycloak(keycloak: KeycloakService) {
return () =>
keycloak.init({
config: {
realm: "master",//设置Realm
url: '/auth', //程序部署到nginx上,该请求将被反向代理到keycloak server。
clientId: 'frontend-client', //设置client
credentials : {secret : "8f28abe0-f4de-476d-bd8b-5354d78639de"}//设置client credentials
},
initOptions: {
onLoad: 'login-required',
flow: 'standard'
},
}).then(() => {
//登录成功
var roleId = "0";
var roleTypes = {"ROLE_ADMIN": "1","ROLE_VIEWER": "2", "ROLE_OPERATOR": "3"};
var roleArray = keycloak.getUserRoles(true); //登录成功,获取当前登录用户的role信息
//登录成功后,将role信息保存到sessionStorage,以后整个应该中随时随处可以使用
if(roleArray.length == 0){
sessionStorage.setItem('userRole', "3");
}else{
roleArray.indexOf("ADMIN_ROLE") != -1 ? sessionStorage.setItem('userRole', "1"): roleArray.indexOf("OPERATOR_ROLE") != -1 ? sessionStorage.setItem('userRole', "2"): sessionStorage.setItem('userRole', "3")
}
console.log("Debug: the current user name is: " + keycloak.getUsername(),)
//得到keycloak 的 access token。
keycloak.getToken().then(data => {
console.log("Debug: the keycloak access token is " + data);
sessionStorage.setItem('accessToken', data);
})
}).catch((error) =>
//登录失败
console.error('Keycloak login failed: ', error)
);
}
在AppModule中添加APP_INITIALIZER provider:
import { KeycloakAngularModule, KeycloakService } from 'keycloak-angular';
import { initializeKeycloak } from './core/services/keycloak-init'; //导入initializeKeycloak工厂函数
@NgModule({
declarations: [AppComponent, FooterComponent, HeaderComponent],
imports: [
BrowserModule,
CoreModule,
SharedModule,
HomeModule,
AuthModule,
AppRoutingModule,
KeycloakAngularModule
],
providers: [
//添加APP_INITIALIZER provider
{
provide: APP_INITIALIZER,
useFactory: initializeKeycloak,
multi: true,
deps: [KeycloakService],
},
],
bootstrap: [AppComponent]
})
Keycloak登录:
Keycloak设置完成之后,启动Angular应用,浏览器中输入 https://localhost/ ,自动重定向到keycloak登录页面。
登录成功之后,每次向后端发送请求,发现header中加入了keycloak access token信息(keycloak-angular提供的HttpClient拦截器会加入access token)。
getBackendInfo(){
this.brucetest = JSON.stringify({"hello":"bruce"});
this.http.get(`/api/adminresource/`).subscribe(
response => {
console.log("Debug: the value of res is " + JSON.stringify(response));
this.brucetest = JSON.stringify(response);
},
err => {
console.log("Debug: error message" + err);
}
)
}
2.自己实现集成Keycloak功能 -- 实现Oauth2.0流程 :
如果担心开源的keycloak-angular库不安全或者有隐患,那么可以自己实现前端keycloak登录功能。
参考视频:【IT老齐211】说人话讲明白OAuth2经典授权码模式_哔哩哔哩_bilibili
本文中Angular应用启动后,自动路由到login组件,我们在login组件中集成keycloak功能(用户自便,什么组件都行)。
首先判断是否登录了keycloak,如果已经登录的话,cookie中会有一个key,叫做"code"(Oauth2.0协议中,授权码模式中的授权码)。如果有这个code,说明用户已经登录成功了(用户登录成功后,会重定向到我们自己的angular页面 redirect_url,重定向地址中带着这个keycloak给我们返回的授权码code)
getCookie(cname) {
var name = cname + "=";
var ca = document.cookie.split(';');
for (var i = 0; i < ca.length; i++) {
if (ca[i].indexOf(name) >= 0) {
console.log(ca[i].slice(ca[i].indexOf(name)));
return ca[i].slice(ca[i].indexOf(name) + 5)
}
}
return "";
}
this.code = this.getCookie('code');
(1).实现Keycloak登录:
然后判断code的值是否为空,如果为空并且程序中没有保存过keycloak access token,说明没有登录(既没有code授权码,又没有access token),则重定向到keycloak登录地址进行登录;如果不为空,说明登录了,获取keycloak 的access token, refresh token等信息。
if (this.code == '') {
this.checkAccessToken();
} else {
this.getAccessToken();
}
//如果code为空,则判断是否保存过keycloak access token,如果没有,则说明没有登录过,需要登录keycloak。
checkAccessToken() {
if (!this.getUser().accessToken) { this.getUser()是自己实现的一个函数,用来访问sessionStorage, 请自行实现,非常简单。
this.redirectKeycloakSSO();
} else {
//已经登录,直接跳转到应用的主界面。
this.router.navigateByUrl('/home');
}
}
//重定向到keycloak登录页面。
redirectKeycloakSSO() {
let url = "/auth/realms/" + this.keycloakRealm + "/protocol/openid-connect/auth";
let params = "redirect_uri=" + this.redirect_uri + "/&client_id=" + this.client_id + "&response_type=" + this.response_type + "&scope=" + this.scope;
window.location.href = url + "?" + params;
}
Note1: 跳转到登录页面的时候,使用到了参数client_id,这个参数都是在keycloak中注册client的时候自动生成给我们的;redirct_url也是在注册client的时候,我们手动填写的。
(2).登录成功,获取Keycloak相关信息:
在keycloak登录页面登录成功后,会自动跳转到我们的angular前端页面(配置keycloak client时配置的redirect_url,并且在这个地址上附带了code授权码),还是路由到login组件,在该组件中会重新执行上面的代码,这次有了code(授权码)将会执行this.getAccessToken(),使用cookie中的code值,访问Django后端,后端去访问keycloak,获取keycloak的access token, refresh token等
Note2:这次会用到client_secret,这个参数也是在keycloak中注册client的时候自动生成给我们的。并且这个client_secret一定要保存到我们自己系统的后端,不能泄漏给前端,这是唯一保证我们用户信息不泄露的保障 ----- 重要 重要 重要,这也是为什么获取access token要调用后端Django API的原因,即使前端的授权码code泄漏了,只要获取不到我们的client_secret,那么就不会有任何的泄漏风险。
Note3:keycloak的登录过程和登录后获取access token的整个流程就是实现了Oauth2.0的授权码模式。
//登录后使用cookie中的code值获取keycloak的access token, refresh token等信息。
getAccessToken() {
let data = {
"redirect_uri": this.redirect_uri + "/",
"code": this.code,
}
jQuery.ajax({
url: this.csfWidgets.backendUrl + "/api/keycloak/token/",
data: JSON.stringify(data),
type: "POST",
async: false,
contentType: 'application/json',
complete: (xhr) => {
},
success: (data, status, xhr) => {
document.cookie = 'params=0;expires=' + new Date(0).toUTCString();
if(! data.hasOwnProperty("access_token")){
console.log('Error: Can not get keycloak access token!');
return;
}
this.getUserInfo(data);
},
error: (data, status, xhr) => {
console.log("Failed: " + JSON.parse(data.responseText).resMsg);
},
fail: (data, status, xhr) => {
console.log("Failed: " + JSON.parse(data.responseText).resMsg);
}
});
}
下面只简单贴一下后端Django程序收到前端发送的request后,是怎样利用code和redirect_uri信息取得keycloak的access token,refresh token等信息。这个get_refresh_token函数肯定是在View中被调用,此处不贴代码了,太啰嗦。
def get_refresh_token(self, code, redirect_uri):
payload = {
"client_id": self.client_id,
"code": code,
"redirect_uri": redirect_uri,
"grant_type": 'authorization_code',
"client_secret": self.client_secret_key
}
#keycloak server的API。
URL = '%s/realms/%s/protocol/openid-connect/token' % (self.server_url,self.realm)
# sending get request and saving the response as response object
try:
#访问keycloak server,获取access token, refresh token等信息。
#reponse = requests.get(url = URL, headers=self.headers,verify=False,proxies=self.proxies)
response = requests.post(url = URL, data=payload,verify=False)
responseData = ""
if response.status_code > 201:
if response.status_code == 403:
responseData = {"res_status":403,"detail":"This user does not have permission to get keycloak refresh token from keycloak."}
elif response.status_code == 401:
responseData = {"res_status":401,"detail": str(response.text)}
else:
responseData = {"res_status":response.status_code,"detail":"Can't get access and refresh token from keycloak server, " + str(response.text)}
return responseData
elif response.headers.get('Content-Type') is not None and 'json' in response.headers.get('Content-Type'):
# extracting data in json format
data = response.json()
return data
else:
responseData = {"res_status":response.status_code,"detail":"Can't get access and refresh token from keycloak server, " + str(response.text)}
return responseData
except Exception as e:
print("Debug: Can't get keycloak refresh token because of: " + str(e))
self.logger.error("Exception found when try to get refresh token from keycloak: " + str(e))
if type(e) == requests.exceptions.ConnectionError :
return {"res_status":500,"detail":"ConnectionError, can't connect the keycloak server."}
return {"res_status":500,"detail":"Can't get keycloak refresh token."}
前端获取到response后,解析response信息,从中获得access token, refresh token等信息,并保存到sessionStorage中,解析access token 得到role信息,并且保存到sessionStorage中,以上操作完成后,认证过程全部完成,跳转到主界面 this.router.navigateByUrl('/home');
//解析response信息,参数data是传入的response信息
getUserInfo(data) {
var tokenInfo = this.decodeToken(data.access_token);
var roleId = "0";
var roleArray = tokenInfo['realm_access']['roles'];
//console.log("Debug: the current user roles is " + roleArray);
if(roleArray.length == 0){
roleId = '3';
}else{
roleArray.indexOf("ADMIN_ROLE") != -1 ? sessionStorage.setItem('userRole', "1"): roleArray.indexOf("OPERATOR_ROLE") != -1 ? sessionStorage.setItem('userRole', "2"): sessionStorage.setItem('userRole', "3");
}
const userinfo = JSON.parse(sessionStorage.getItem('user'));
const user = userinfo ? userinfo : {};
Object.assign(user, {
username: tokenInfo['preferred_username'],
roleId: roleId,
passwordFlag: false, //Don't use original method to change the admin user password.
});
// this.role = data.roles;
const keycloakAuthInfo = {
"accessToken": data.access_token,
"refreshToken": data.refresh_token
};
//将信息保存到sessionStorage中。
this.updateUser(keycloakAuthInfo);
this.updateUser(user);
//将access token, refresh token信息保存到cookie中,这样以后每次向后端发送http请求,都会自动带上access token信息。
this.csfWidgets.setCookie("keycloakToken=" + data.access_token);
//所以认证授权工作完成之后,跳转到主界面
this.router.navigateByUrl('/home');
}
至此,所有的认证工作均已完成,系统可以在keycloak保护下正常工作了,前端发送的每个http请求,都会带上access token信息(cookie中设置了access token,所有http请求都会带上这个access token,为了防止CSRF跨站请求伪造攻击,把这个access token放到每个http请求的header中,例如Authorization这个自己命名的header,后端也要校验这个header,不要校验cookie中的access token),后端Django中间件会检查这个access token是否有效,如果有效,则执行业务代码,正常工作;如果失效(例如过期),则返回401错误,让用户重新登录。
Note3: keycloak的登录过程和登录后获取access token的整个流程就是实现了Oauth2.0的授权码模式。
3.关于Nginx配置:
前端在发送登录请求的时候,nginx会通过反向代理将请求转发到keycloak server,从而重定向到keycloak登录页面,登录后再获取access token,但此时token中的issuer地址是nginx地址,后端使用这个access token访问keycloak server的时候,会要求issuer地址是keycloak server地址,而不是nginx地址,这就需要nginx的反向代理来解决,具体请参考:通过nginx访问keycloak时的Invalid token issuer问题_知难行难1985的博客-CSDN博客_keycloak nginx
后端Django集成Keycloak:
前面大体介绍了一下整个keycloak认证的整个过程,包括前端和后端的流程。下面详细介绍后端的实现。
主要是需要实现一个中间件,作用是拦截来自Angular前端的每个请求,进行认证检查。从request的Header获取keycloak access token,利用这个token去访问keycloak server,如果能够成功,说明用户登录了,中间件返回"None",继续执行View,进行业务处理;如果失败,说明用户没有登录,中间件直接返回HttpResponse,请求完毕,不再继续进行业务处理。
下图是中间件工作原理:
1.实现中间件:
在settings.py中配置Keycloak信息:
KEYCLOAK_CONFIG = {
'KEYCLOAK_SERVER_URL': 'https://127.0.0.1:8443/auth',
#跟前端保持一致,使用相同的Realm和client
'KEYCLOAK_REALM': 'master',
'KEYCLOAK_CLIENT_ID': 'frontend-client',
'KEYCLOAK_CLIENT_SECRET_KEY': '8f28abe0-f4de-476d-bd8b-5354d78639de',
'KEYCLOAK_BEARER_AUTHENTICATION_EXEMPT_PATHS':[r'api/keycloak/token/*', r'api/keycloak/config/*',r'api/automation/token/*',r'api/keycloak/logout/'] #python Regular expression
}
其中配置在"KEYCLOAK_BEARER_AUTHENTICATION_EXEMPT_PATHS"中的url,中间件不会进行用户认证检查,例如使用code获取access token信息的"api/keycloak/token/", 因为是前端登录之后第一次访问后端,此时还未获得access token信息,所以不进行认证检查。
中间件的具体实现:具体细节看代码注释
class KeycloakMiddleware(MiddlewareMixin):
def __init__(self, get_response):
"""
:param get_response:
"""
self.config = settings.KEYCLOAK_CONFIG
# Read configurations
try:
self.server_url = self.config['KEYCLOAK_SERVER_URL']
self.client_id = self.config['KEYCLOAK_CLIENT_ID']
self.realm = self.config['KEYCLOAK_REALM']
except KeyError as e:
raise Exception("KEYCLOAK_SERVER_URL, KEYCLOAK_CLIENT_ID or KEYCLOAK_REALM not found.")
self.client_secret_key = self.config.get('KEYCLOAK_CLIENT_SECRET_KEY', None)
self.client_public_key = self.config.get('KEYCLOAK_CLIENT_PUBLIC_KEY', None)
self.default_access = self.config.get('KEYCLOAK_DEFAULT_ACCESS', "DENY")
self.method_validate_token = self.config.get('KEYCLOAK_METHOD_VALIDATE_TOKEN', "INTROSPECT")
self.keycloak_authorization_config = self.config.get('KEYCLOAK_AUTHORIZATION_CONFIG', None)
self.keycloak_bearer_authentication_exempts = self.config.get('KEYCLOAK_BEARER_AUTHENTICATION_EXEMPT_PATHS', None)
# Django
self.get_response = get_response
#lock
self.lock = threading.Lock()
.........................................................
def process_view(self, request, view_func, view_args, view_kwargs):
"""
Validate only the token introspect.
:param request: django request
:param view_func:
:param view_args: view args
:param view_kwargs: view kwargs
:return:
"""
#Check if this request path is no authentication required, if yes, go to views.
if len(self.keycloak_bearer_authentication_exempts) > 0:
path = request.path_info.lstrip('/')
print("Debug: the url path is " + path)
if any(re.match(m, path) for m in
self.keycloak_bearer_authentication_exempts):
logger.debug('** exclude path found, skipping')
return None
accessToken = "";
#如果Header中没有token信息'HTTP_AUTHORIZATION',则判断'HTTP_COOKIE'中有没有
if 'HTTP_AUTHORIZATION' not in request.META:
if not request.META.get('HTTP_COOKIE'):
print("Debug: can't get the cookie keycloak token");
return JsonResponse({"detail": NotAuthenticated.default_detail},
status=NotAuthenticated.status_code)
else:
#得到token信息
accessToken =request.COOKIES["keycloakToken"];
print("Debug: the request cookie token is: " + accessToken);
#如果Header中有token信息'HTTP_AUTHORIZATION',则从'HTTP_AUTHORIZATION'中获取token。
if not accessToken.strip():
auth_header = request.META.get('HTTP_AUTHORIZATION').split()
accessToken = auth_header[1] if len(auth_header) == 2 else auth_header[0]
print("Debug: the request token is " + accessToken);
try:
#使用keycloak API工具类KeycloakHttpRequest,访问keycloak server获取用户信息
keycloakHttpRequest = KeycloakHttpRequest(self.server_url,self.client_secret_key,self.realm,self.client_id)
#访问keycloak server,得到登录用户信息
keycloakUserInfo = keycloakHttpRequest.get_userinfo(accessToken)
print("Debug: the keycloakUserInfo is: " + json.dumps(keycloakUserInfo))
keycloakUserRoles=''
#print("Debug: the userInfoData username is %s, user ID is %s " % (userinfo['preferred_username'],userinfo['sub']))
if keycloakUserInfo is not None:
if "res_status" in keycloakUserInfo:
return JsonResponse({"detail": keycloakUserInfo['detail']},status=keycloakUserInfo['res_status'])
if 'user_roles' in keycloakUserInfo:
# 从用户信息中获取role信息。
keycloakUserRoles = keycloakUserInfo['user_roles']
request.META['USER_ROLES'] = ','.join(keycloakUserRoles)
else:
#Get user roles(User must have the Client Roles 'view-users', otherwise there will be exception.)
print("Debug: current user is: " + keycloakUserInfo['sub'])
//如果用户信息keycloakUserInfo中没有role信息,则直接访问keycloak server获取
roles = keycloakHttpRequest.get_user_roles(keycloakUserInfo['sub'],accessToken)
if "res_status" in roles:
return JsonResponse({"detail": roles['detail']},status=roles['res_status'])
if roles is None or not len(roles):
keycloakUserRoles = None
request.META['USER_ROLES'] = 'VIEWE_ROLE'
else:
keycloakUserRoles = [user['name'] for user in roles]
request.META['USER_ROLES'] = ','.join(keycloakUserRoles)
在keycloak中间件实现之后, 将其加入到settings.py中的MIDDLEWARE列表中.
2.实现Keycloak的API工具类:
工具类KeycloakHttpRequest.py调用各种API(带上access token)访问keycloak server,获取需要的信息,例如获取用户信息,得到用户role信息,使用refresh token刷新access token,keycloak登出等等。此工具类是供中间件(例如 获取用户信息)和View(例如 keycloak登出)使用的。
class KeycloakHttpRequest(object):
def __init__(self,server_url,client_secret_key,realm='master',client_id='admin-cli'):
if server_url.endswith('/'):
self.server_url = server_url[:-1]
else:
self.server_url = server_url
self.realm = realm
self.client_id = client_id
self.client_secret_key = client_secret_key
self.proxies={
'http':'http://cnproxy.int.nokia-sbell.com/proxy.pac',
'https':'http://cnproxy.int.nokia-sbell.com/proxy.pac'
}
def get_userinfo(self, accessToken):
headers={
'Authorization': 'Bearer ' + accessToken,
'Content-Type': 'application/json'
}
#http://$KC_SERVER/$KC_CONTEXT/realms/$KC_REALM/protocol/openid-connect/userinfo"
userInfoUrl = '%s/realms/%s/protocol/openid-connect/userinfo' % (self.server_url,self.realm)
try:
#userInfoResponse = requests.get(url = userInfoUrl, headers=self.headers,verify=False,proxies=self.proxies)
userInfoResponse = requests.get(url = userInfoUrl, headers=headers,verify=False)
userInfoData=''
print("debug: the userinfo reponse info is : " + str(userInfoResponse))
if userInfoResponse.headers.get('Content-Type') is not None and 'json' in userInfoResponse.headers.get('Content-Type'):
userInfoData = userInfoResponse.json()
else:
if userInfoResponse.status_code == 403:
userInfoData = {"res_status":403,"detail":"This user does not have permission to access the user info from keycloak."}
elif userInfoResponse.status_code == 401:
userInfoData = {"res_status":401,"detail": str(userInfoResponse)}
else:
userInfoData = {"res_status":userInfoResponse.status_code,"detail":"Can't get the user info from keycloak server."}
return userInfoData
except Exception as e:
print("Debug: the error message: " + str(e))
print("Debug: the type of Exception : " + str(type(e)))
if type(e) == requests.exceptions.ConnectionError :
return {"res_status":500,"detail":"ConnectionError, can't connect the keycloak server."}
return {"res_status":401,"detail":"Can't get the user info from keycloak server."}
def get_user_roles(self,user_uuid, accessToken):
headers={
'Authorization': 'Bearer ' + accessToken,
'Content-Type': 'application/json'
}
#/auth/admin/realms/{realm}/users/{user-uuid}/role-mappings/realm
URL = '%s/admin/realms/%s/users/%s/role-mappings/realm' % (self.server_url,self.realm,user_uuid)
# sending get request and saving the response as response object
try:
# print("Debug: try to get user roles from keycloak server " + URL)
#reponse = requests.get(url = URL, headers=self.headers,verify=False,proxies=self.proxies)
response = requests.get(url = URL, headers=headers,verify=False)
print("Debug: user roles reponse string is:" + str(response.headers))
print("Debug: user roles reponse string is:" + str(response.headers.get('Content-Type')))
userRoleData=''
if response.headers.get('Content-Type') is not None and 'json' in response.headers.get('Content-Type'):
# extracting data in json format
# print("Debug: user roles reponse string is:" + str(response))
data = response.json()
# print("Debug: the user roles info is " + str(data))
return data
else:
if response.status_code == 403:
userRoleData = {"res_status":403,"detail":"This user does not have permission to get the user role info from keycloak."}
elif response.status_code == 401:
userRoleData = {"res_status":401,"detail": str(response)}
else:
userRoleData = {"res_status":response.status_code,"detail":"Can't get the user role info from keycloak server."}
return userRoleData
except Exception as e:
print("Debug: the error message: " + str(e))
print("Debug: the type of Exception : " + str(type(e)))
if type(e) == requests.exceptions.ConnectionError :
return {"res_status":500,"detail":"ConnectionError, can't connect the keycloak server."}
return {"res_status":500,"detail":"Can't get the user role info from keycloak server."}
代码地址:
本文代码github地址:GitHub - wdquan1985/Angular-Django-Keycloak
前端集成keycloak使用方式一,使用开源的keycloak-angular库。用户可以根据上面提供的第二种方式自己实现集成keycloak。前端提供的主界面及其简单,因为完全没必要加入其它任何业务,只是完成keycloak登录后,进入主界面,发送一个http请求,然后在主界面将response显示出来就好。