首页 > 解决方案 > 我们如何在 Java Springboot 中动态使用 Policy-enforcer?

问题描述

Saurav Chaurasia 周五,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=外部

keycloak.resource=课程管理

keycloak.bearer-only=true

keycloak.credentials.secret=a5df9621-73c9-4e0e-9d7a-97e9c692a930

keycloak.securityConstraints[0].authRoles[0]=老师

keycloak.securityConstraints[0].authRoles[1]=ta

keycloak.securityConstraints[0].authRoles[2]=学生

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

配置.class

package com.lantana.school.course.coursemanagment.security; 

`导入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();

}

}`

控制器类

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;

导入 org.springframework.web.servlet.support.ServletUriComponentsBuilder;

导入 com.fasterxml.jackson.core.JsonProcessingException;

导入 com.lantana.school.course.coursemanagment.security.Identity;

@RestController

公共类课程控制器{

   @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();

////// 列表头 = 令牌。;

// 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());

   }

}

服务类

import java.nio.charset.StandardCharsets;

导入 java.util.ArrayList;

//导入java.util.Iterator;

导入 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;

@零件

公共类 CourseService {

   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", "introduction", "term1");

          Course cs2003 = new Course("CS2003", "Data Management and Data Systems", "introduction", "term2");

          Course cs2004 = new Course("CS2004", "Introduction to Computer Graphics and Imaging", "introduction", "term3");

          Course cs2005 = new Course("CS2005", "Design and Analysis of Algorithms", "introduction", "term4");

          Course cs2006 = new Course("CS2006", "Analysis of Networks", "introduction", "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);

// return 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("解码:" + 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);

       }

// 列表动作=new ArrayList();

// 映射角色=((Map)jo.get("realm_access"));

// Iterator<Map.Entry> itr1=roles.entrySet().iterator();

// while(itr1.hasNext())

// {

// Map.Entry pair=itr1.next();

// System.out.println(pair);

// }

       return role;   

   }

  

  

}

模型类

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;

   }

}

hatos 自定义类

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,Model model,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, model,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);

   }

}

标签: spring-bootkeycloakkeycloak-serviceskeycloak-rest-apikeycloak-connect

解决方案


如果您使用的是 Keycloak 的授权服务(如果您使用的是 PEP),则不必在 spring boot keycloak-configuration 中定义角色。请注意角色不是策略执行器配置的一部分。如果您只是删除keycloak.securityConstraints[0].authRoles,然后在 Keycloak 服务器上检查您的策略中的角色,那么您应该很好。

至于资源路径,我看到你已经注释掉了keycloak.policy-enforcer-config.lazy-load-paths。与. http-method-as-scope_ @PostMapping("/courses")(您必须在 HTTP 方法之后在 Keycloak 中命名您的范围才能使其工作)。在这种情况下,您实际上是在使用默认的 PEP 配置,除了为您的应用程序启用策略强制之外,您无需指定任何其他内容。


推荐阅读