为Keycloak授权服务器实现自定义分支策略及专属UI
一、完善后端SPI配置
你已经实现了基础的PolicyProvider和Representation,现在需要补充PolicyProviderFactory的配置,让Keycloak关联前端组件并处理策略参数:
添加策略配置属性
重写getConfigProperties()方法,定义分支列表等配置项,让后端能接收前端传递的参数:@Override public List<ConfigProperty> getConfigProperties() { List<ConfigProperty> properties = new ArrayList<>(); properties.add(new ConfigProperty( "allowedBranches", "Allowed Branches", "List of branches authorized to access the resource", ConfigProperty.MULTIVALUED_STRING_TYPE, null )); return properties; }指定前端资源路径
重写getFrontendPath()方法,指向你的前端组件文件:@Override public String getFrontendPath() { return "/js/custom-branch-policy.js"; }补充策略元数据
重写以下方法,让UI显示正确的分类和名称:@Override public String getDisplayName() { return "Custom Branch Policy"; } @Override public String getDescription() { return "Grants access based on user's branch affiliation"; } @Override public String getPolicyType() { return "custom-branch"; } @Override public String getGroup() { return "Custom"; // 若想和角色策略同组,可改为"Role" }同步策略配置逻辑
在createPolicy和updatePolicy中,将前端传递的配置映射到自定义Representation:@Override public Policy createPolicy(PolicyRepresentation representation, AuthorizationProvider authorization) { TosanBranchPolicyRepresentationExt branchRep = (TosanBranchPolicyRepresentationExt) representation; Policy policy = authorization.getStoreFactory().getPolicyStore().create(representation); policy.getConfig().put("allowedBranches", branchRep.getAllowedBranches()); return policy; } @Override public void updatePolicy(Policy policy, PolicyRepresentation representation, AuthorizationProvider authorization) { TosanBranchPolicyRepresentationExt branchRep = (TosanBranchPolicyRepresentationExt) representation; policy.getConfig().put("allowedBranches", branchRep.getAllowedBranches()); authorization.getStoreFactory().getPolicyStore().update(policy); }同时确保
TosanBranchPolicyRepresentationExt包含分支列表字段及getter/setter:public class TosanBranchPolicyRepresentationExt extends AbstractPolicyRepresentation { private List<String> allowedBranches; public List<String> getAllowedBranches() { return allowedBranches; } public void setAllowedBranches(List<String> allowedBranches) { this.allowedBranches = allowedBranches; } }
二、开发前端UI组件
Keycloak管理控制台基于React和AMD模块系统,需开发符合规范的前端组件实现分支选择交互:
创建前端组件文件
在项目src/main/resources/META-INF/resources/keycloak/js/目录下创建custom-branch-policy.js,参考Keycloak内置策略组件结构实现:define([ 'react', 'keycloak-ui/core', 'keycloak-ui/components/policy-editor/common' ], function(React, Core, Common) { const { Form, FormGroup, MultiSelect } = Core; const { PolicyEditorBase } = Common; class CustomBranchPolicyEditor extends PolicyEditorBase { constructor(props) { super(props); this.state = { allowedBranches: this.props.policy.config?.allowedBranches || [] }; } handleBranchChange = (value) => { this.setState({ allowedBranches: value }); this.props.onChange({ ...this.props.policy, config: { ...this.props.policy.config, allowedBranches: value } }); }; render() { // 可替换为从后端接口动态获取的分支列表 const branchOptions = [ { value: 'branch-shanghai', label: 'Shanghai Branch' }, { value: 'branch-beijing', label: 'Beijing Branch' }, { value: 'branch-guangzhou', label: 'Guangzhou Branch' } ]; return ( <Form> <FormGroup label="Allowed Branches" helpText="Select branches that can access the resource" > <MultiSelect options={branchOptions} value={this.state.allowedBranches} onChange={this.handleBranchChange} isMulti /> </FormGroup> </Form> ); } } // 注册策略编辑器到Keycloak keycloak.provider.registerPolicyEditor({ type: 'custom-branch', // 和后端getPolicyType()返回值一致 group: 'Custom', // 和后端getGroup()返回值一致 editor: CustomBranchPolicyEditor, displayName: 'Custom Branch Policy', description: 'Grants access based on user\'s branch affiliation' }); return CustomBranchPolicyEditor; });打包前端资源
若使用ES6/React语法,需用Webpack或Rollup将代码打包为AMD模块,配置时将react、keycloak-ui等依赖外部化,避免重复打包。
三、部署与测试
打包部署
将后端代码编译为jar包,确保前端资源custom-branch-policy.js位于jar包的META-INF/resources/keycloak/js/路径下,复制jar包到Keycloak的providers目录,执行kc.sh build(Windows用kc.bat build)后重启服务器。验证UI与逻辑
登录Keycloak管理控制台,进入授权标签页创建新策略,选择"Custom Branch Policy"即可看到自定义分支选择UI。同时需修改CustomBranchPolicyProvider的evaluate方法,从策略配置中读取allowedBranches来判断用户权限:@Override public void evaluate(Evaluation evaluation) { EvaluationContext context = evaluation.getContext(); Identity identity = context.getIdentity(); Attributes attributes = identity.getAttributes(); String username = attributes.getValue("username").asString(0); Policy policy = evaluation.getPolicy(); List<String> allowedBranches = policy.getConfig().getList("allowedBranches"); if (allowedBranches != null && HasBranch(username, allowedBranches)) { evaluation.grant(); } else { evaluation.deny(); } }
内容的提问来源于stack exchange,提问作者Javad Malekzaseh

