参考官方开发示例,使用Java为Keycloak系统编写一套个人隐私问题的认证模块
代码编写完成后,借助Maven将项目打包为jar文件,传输至集群主节点准备部署
注意在打包时需要添加
resources/META-INF/services
目录,并注册Factory类
导入插件
首先创建初始ConfigMap:
kubectl create configmap secret-question-plugin --from-file=SecretQuestion.jar=./kc-plugins/SecretQuestion.jar -n keycloak
然后执行kubectl edit statefulset keycloak -n keycloak
对keycloak的StatefulSet进行修改:
spec:
template:
spec:
volumes:
- name: plugins-volume
configMap:
name: secret-question-plugin
containers:
- name: keycloak
# 其他配置...
volumeMounts:
- name: plugins-volume
mountPath: /opt/keycloak/providers/SecretQuestion.jar
subPath: SecretQuestion.jar
更新插件
之后每次更新迭代插件时,需要先删除原ConfigMap,然后创建新ConfigMap,最后重启Pod即可
# 删除旧的ConfigMap
kubectl delete configmap secret-question-plugin -n keycloak
# 创建新的ConfigMap
kubectl create configmap secret-question-plugin --from-file=SecretQuestion.jar=./kc-plugins/SecretQuestion.jar -n keycloak
# 重启Keycloak Pod
kubectl delete pod keycloak-0 -n keycloak
插件代码解析
将插件导入Keycloak系统后,需要先在【身份验证】【必需的操作】处将之开启,然后将自定义的认证执行器插入流程之中,如此才能使新认证功能发挥作用
这里将新认证器放置在“账户密码验证”之后且作为必需行动
-
当用户首先进入认证界面(账户密码界面)时,仅账户密码认证执行器在发挥作用
-
当用户点击登录按钮并通过,根据流程,系统应该执行必需的密保问题验证:
-
调用
Authenticator
中的configuredFor()
方法,检查用户是否已经配置过相应凭证关于用户是否配置过相应凭证,可以在UI界面的用户详细信息中查看,一般未配置的话只有password口令凭证
-
若
configuredFor()
返回true
,代表用户配置过这种凭证,则调用此类的authenticate()
方法正式进入认证:authenticate()
向用户呈现挑战页面,用户输入答案点击提交后调用action()
方法action()
方法从上下文获取用户输入的答案并封装,交由Provider
提供的isValid()
方法验证答案正确性- 若答案错误,则构建失败页面并呈现,将上下文设置为失败状态
- 若答案正确,则将上下文设置为成功状态
-
若
configuredFor()
返回false
,代表用户从未配置过这种凭证,但系统流程中又设置了“必需”执行此操作,因此会调用此类的setRequiredActions()
中所注册的Action相应方法进行操作(此时的情况是需要用户现场创建一个此类型凭证并储存)setRequiredActions()
中使用一个ID
字符串来注册操作,这个ID
定义在实现了RequiredActionProvider
接口的RequiredAction
类中,作为静态不可修改变量RequiredAction
类调用requiredActionChallenge()
方法呈现一个添加密保问题的界面,用户输入问题答案后点击提交调用processAction()
方法processAction()
方法从上下文提取用户输入信息,再从上下文的session
中提取Provider
,调用Provider
中存储新凭证的createCredential()
方法即可保存凭证- 将上下文设置为成功状态
-
-
用户通过密保问题,完成认证
如此来看,Authenticator
类、RequiredAction
类以及相应的两个Factory
类都是工作在逻辑上的业务层以及应用层的
而CredentialModel
和CredentialProvider
则是工作在数据层,其中CredentialModel
更偏向POJO的感觉,仅仅是负责在内存中保存、装填或获取Model
对象;CredentialProvider
则负责信息的交互与存储,包括检查是否已配置凭证、检查答案与存储答案是否一致、在数据库中增加或删除凭证等操作,这些都是需要直接与数据库进行交互查询的(虽然查询交互过程也被提前封装了)