如何解决我们如何在 Java Springboot 中动态使用 Policy-enforcer?
索拉夫·乔拉西亚 5 月 7 日星期五下午 5:00(20 小时前) 给我
就像我能够通过向 application.properties 提供路径、方法和资源来使用静态策略执行器。但是在实时应用程序中,我们将拥有 N 个角色,我们将拥有 N 个 API,我们将在 KeyCloak 的资源、策略和权限中提供这些 API。将来,如果我们希望将更多角色添加到 keycloak 中,并且将在必要的权限下添加新资源。我们不会回到我们的 springboot 来更改资源和角色的代码。 Spring Boot 的所有权限检查都应该是动态的,我们该怎么做,请帮帮我。
我目前使用的静态方法对少数角色工作正常。我们正在向 keyCloak 添加更多角色。
下面是静态代码。
application.properties
server.port = 8090
keycloak.realm=大学
keycloak.auth-server-url=http://localhost:8080/auth
keycloak.ssl-required=external
keycloak.resource=课程管理
keycloak.bearer-only=true
keycloak.credentials.secret=a5df9621-73c9-4e0e-9d7a-97e9c692a930
keycloak.securityConstraints[0].authRoles[0]=teacher
keycloak.securityConstraints[0].authRoles[1]=ta
keycloak.securityConstraints[0].authRoles[2]=student
keycloak.securityConstraints[0].authRoles[3]=parent
keycloak.securityConstraints[0].securityCollections[0].name=课程管理
keycloak.securityConstraints[0].securityCollections[0].patterns[0] = /courses/get/*
#keycloak.policy-enforcer-config.lazy-load-paths=true
keycloak.policy-enforcer-config.paths[0].path=/courses/get/*
keycloak.policy-enforcer-config.paths[0].methods[0].method=GET
keycloak.policy-enforcer-config.paths[0].methods[0].scopes[0]=view
keycloak.policy-enforcer-config.paths[0].methods[1].method=DELETE
keycloak.policy-enforcer-config.paths[0].methods[1].scopes[0]=delete
Configuration.class
package com.lantana.school.course.coursemanagment.security;
`import java.util.List;
导入 org.keycloak.AuthorizationContext;
导入 org.keycloak.KeycloakSecurityContext;
导入 org.keycloak.representations.idm.authorization.Permission;
公共类身份{
private final KeycloakSecurityContext securityContext;
public Identity(KeycloakSecurityContext securityContext) {
this.securityContext = securityContext;
}
/**
* An example on how you can use the {@link org.keycloak.AuthorizationContext} to check for permissions granted by Keycloak for a particular user.
*
* @param name the name of the resource
* @return true if user has was granted with a permission for the given resource. Otherwise,false.
*/
public boolean hasResourcePermission(String name) {
System.out.println("Permission: "+getAuthorizationContext().hasResourcePermission(name));
return getAuthorizationContext().hasResourcePermission(name);
}
/**
* An example on how you can use {@link KeycloakSecurityContext} to obtain information about user's identity.
*
* @return the user name
*/
public String getName() {
System.out.println("UserName: "+securityContext.getIdToken().getPreferredUsername());
return securityContext.getIdToken().getPreferredUsername();
}
/**
* An example on how you can use the {@link org.keycloak.AuthorizationContext} to obtain all permissions granted for a particular user.
*
* @return
*/
public List<Permission> getPermissions() {
System.out.println("Permission 2: "+getAuthorizationContext().getPermissions());
return getAuthorizationContext().getPermissions();
}
/**
* Returns a {@link AuthorizationContext} instance holding all permissions granted for an user. The instance is build based on
* the permissions returned by Keycloak. For this particular application,we use the Entitlement API to obtain permissions for every single
* resource on the server.
*
* @return
*/
private AuthorizationContext getAuthorizationContext() {
System.out.println("getAuthorizationContext: "+ securityContext.getAuthorizationContext());
return securityContext.getAuthorizationContext();
}
}`
Controller.class
package com.lantana.school.course.coursemanagment.services;
导入 java.math.BigInteger;
导入 java.net.URI;
导入 java.util.ArrayList;
导入 java.util.List;
导入 javax.servlet.http.HttpServletRequest;
导入 org.keycloak.KeycloakSecurityContext;
导入 org.springframework.beans.factory.annotation.Autowired;
导入 org.springframework.hateoas.EntityModel;
//导入org.springframework.hateoas.Link;
//导入org.springframework.hateoas.server.mvc.WebMvcLinkBuilder;
//导入org.springframework.http.HttpHeaders;
导入 org.springframework.http.MediaType;
导入 org.springframework.http.ResponseEntity;
导入 org.springframework.ui.Model;
导入 org.springframework.web.bind.annotation.DeleteMapping;
导入 org.springframework.web.bind.annotation.GetMapping;
导入 org.springframework.web.bind.annotation.PathVariable;
导入 org.springframework.web.bind.annotation.PostMapping;
导入 org.springframework.web.bind.annotation.RequestBody;
导入 org.springframework.web.bind.annotation.RequestHeader;
导入 org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
导入 com.fasterxml.jackson.core.JsonProcessingException;
导入 com.lantana.school.course.coursemanagment.security.Identity;
@RestController
公共类 CourseController{
@Autowired
private HttpServletRequest request;
@Autowired
private CourseService couseService;
@Autowired
private hateo hatoeslink;
List<String> rol=new ArrayList<String>();
//
// @GetMapping(value = "/courses/api")
// public String generateApi(@RequestHeader("Authorization") String token){
////// rol.clear();
////// List headers = token.;
// System.out.println("Token: "+token);
////// System.out.println("角色控制器:"+rol);
////// rol=couseService.getRole(token);
////// List> link=new ArrayList();
//////
// return new String("Role Fetched");
// }
@GetMapping(value = "/courses/get/{id}",produces = MediaType.APPLICATION_JSON_VALUE)
public EntityModel<Course> getCourse(@PathVariable("id") long id,Model model,@RequestHeader("Authorization") String token) throws JsonProcessingException {
configCommonAttributes(model);
rol=couseService.getRole(token);
System.out.println("GetRole: "+rol);
Course course = couseService.getCourse(id);
hatoeslink.hateoLink(rol,id,model,token);
return EntityModel.of(course);
}
@DeleteMapping("/courses/delete/{id}")
public EntityModel<Course> deleteStudent(@PathVariable long id) {
System.out.println("calling delete operation");
//
Course course = couseService.getCourse(id);
couseService.deleteById(id);
return EntityModel.of(course);
}
@PostMapping("/courses")
public ResponseEntity<Course> createCourse(@RequestBody Course course) {
Course savedCourse = couseService.addCourse(course);
URI location = ServletUriComponentsBuilder.fromCurrentRequest().path("/{id}")
.buildAndExpand(savedCourse.getCode()).toUri();
return ResponseEntity.created(location).build();
}
private void configCommonAttributes(Model model) {
model.addAttribute("identity",new Identity(getKeycloakSecurityContext()));
}
private KeycloakSecurityContext getKeycloakSecurityContext() {
return (KeycloakSecurityContext) request.getAttribute(KeycloakSecurityContext.class.getName());
}
}
Service.class
import java.nio.charset.StandardCharsets;
导入 java.util.ArrayList;
//导入java.util.Iterator;
import java.util.LinkedHashMap;
导入 java.util.List;
导入 java.util.Map;
导入 org.apache.commons.codec.binary.Base64;
导入 org.json.JSONArray;
导入 org.json.JSONObject;
导入 org.springframework.stereotype.Component;
@组件
公共课程课程服务{
public static final Map<Long,Course> courseMap = new LinkedHashMap<Long,Course>();
static {
Course cs2001 = new Course("CS2001","Mathematical Foundations of Computing","introduction","term1");
Course cs2002 = new Course("CS2002","Computer Organization and Systems","term1");
Course cs2003 = new Course("CS2003","Data Management and Data Systems","term2");
Course cs2004 = new Course("CS2004","Introduction to Computer Graphics and Imaging","term3");
Course cs2005 = new Course("CS2005","Design and Analysis of Algorithms","term4");
Course cs2006 = new Course("CS2006","Analysis of Networks","term4");
courseMap.put(cs2001.getId(),cs2001);
courseMap.put(cs2002.getId(),cs2002);
courseMap.put(cs2003.getId(),cs2003);
courseMap.put(cs2004.getId(),cs2004);
courseMap.put(cs2005.getId(),cs2005);
courseMap.put(cs2006.getId(),cs2006);
}
public Course getCourse(Long id) {
return courseMap.get(id);
}
public Course addCourse(Course course) {
courseMap.put(course.getId(),course);
return course;
}
public void deleteById(long id) {
courseMap.remove(id);
//返回id+"删除成功";
}
public List<String> getRole(String token) {
String[] payload=token.split("\\.");
byte[] bytes = Base64.decodeBase64(payload[1]);
String decodedString = new String(bytes,StandardCharsets.UTF_8);
// System.out.println("Decoded: " + decodedString);
JSONObject jo=new JSONObject(decodedString);
JSONObject obj=jo.getJSONObject("realm_access");
JSONArray jArray=obj.getJSONArray("roles");
System.out.println(obj);
System.out.println(jArray);
List<String> role=new ArrayList();
for(int i=0;i<jArray.length();i++)
{ String ro=(String) jArray.get(i);
System.out.println(jArray.get(i));
role.add(ro);
}
// List action=new ArrayList();
//映射角色=((Map)jo.get("realm_access"));
// Iterator
// while(itr1.hasNext())
// {
// Map.Entry pair=itr1.next();
// System.out.println(pair);
// }
return role;
}
}
模型.class
package com.lantana.school.course.coursemanagment.services;
导入 org.springframework.hateoas.RepresentationModel;
公共课程课程扩展了 RepresentationModel {
private static long nextID = 1000;
public Course(String code,String name,String modules,String enrollmentTerm) {
super();
this.id = nextID++;
this.code = code;
this.name = name;
this.modules = modules;
this.enrollmentTerm = enrollmentTerm;
}
Long id;
String code;
String name;
String modules;
String enrollmentTerm;
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getModules() {
return modules;
}
public void setModules(String modules) {
this.modules = modules;
}
public String getEnrollmentTerm() {
return enrollmentTerm;
}
public void setEnrollmentTerm(String enrollmentTerm) {
this.enrollmentTerm = enrollmentTerm;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
}
hateos 自定义类
package com.lantana.school.course.coursemanagment.services;
导入 java.util.List;
导入 org.springframework.beans.factory.annotation.Autowired;
导入 org.springframework.hateoas.EntityModel;
导入 org.springframework.hateoas.server.mvc.WebMvcLinkBuilder;
导入 org.springframework.stereotype.Component;
导入 org.springframework.ui.Model;
导入 com.fasterxml.jackson.core.JsonProcessingException;
@组件
公开课仇恨{
@Autowired
private CourseService couseService;
public EntityModel<Course> hateoLink(List<String> role,long id,String token)
{ Course course = couseService.getCourse(id);
course.removeLinks();
role.stream().forEach(action ->{
if(action.equalsIgnoreCase("teacher"))
{
// course.removeLinks();
course.add(WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodon(CourseController.class).createCourse(course)).withRel("add"));
}
if(action.equalsIgnoreCase("student"))
{
try {
course.add(WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodon(CourseController.class).getCourse(id,token)).withRel("view"));
} catch (JsonProcessingException e) {
// Todo Auto-generated catch block
e.printstacktrace();
}
}
if(action.equalsIgnoreCase("parent"))
{
course.add(WebMvcLinkBuilder.linkTo(WebMvcLinkBuilder.methodon(CourseController.class).deleteStudent(id)).withRel("delete"));
}
});
return EntityModel.of(course);
}
}
解决方法
如果您使用 Keycloak 的授权服务(如果您使用的是 PEP),则不必在 spring boot keycloak-configuration 中定义角色。请注意角色不是 policy-enforcer-config 的一部分。如果您只是删除 keycloak.securityConstraints[0].authRoles
,而是在 Keycloak 服务器上检查您的策略中的角色,那么您应该没问题。
至于资源路径,我看到您已经注释掉了 keycloak.policy-enforcer-config.lazy-load-paths
。将此与 http-method-as-scope
一起使用,您不必为您的资源提供任何其他配置,因为 Keycloak 适配器会自动从 @PostMapping("/courses")
之类的注释中获取该配置。 (您必须在 HTTP 方法之后在 Keycloak 中命名您的范围才能使其工作)。在这种情况下,您实际上使用的是默认的 PEP 配置,除了为您的应用程序启用策略实施之外,您无需指定任何其他内容。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。