sourcecode

Security for ( Spring has Permission for Collection ) 。 Security for ( Spring has Permission for Collection ) 。 Security for ( Spring has Permission for Collection ) 。동작 중인 어플리케이션이 메서드 수준의 보..

copyscript 2023. 3. 15. 19:53
반응형

Security for ( Spring has Permission for Collection ) 。

동작 중인 어플리케이션이 메서드 수준의 보안으로 보호되어 있다.

Rest Controller:

@PreAuthorize("hasPermission(#product, 'WRITE')")
@RequestMapping(value = "/save", method = RequestMethod.POST)
public Product save(@RequestBody Product product) {
    return productService.save(product);
}

Permission Evaluator:

public class SecurityPermissionEvaluator implements PermissionEvaluator {

    private Logger log = LoggerFactory.getLogger(SecurityPermissionEvaluator.class);

    private final PermissionService permissionService;

    public SecurityPermissionEvaluator(PermissionService permissionService) {
        this.permissionService = permissionService;
    }

    @Override
    public boolean hasPermission(Authentication authentication, Object targetDomainObject, Object permission) {
        CustomUserDetails userDetails = (CustomUserDetails) authentication.getPrincipal();
        return permissionService.isAuthorized(userDetails.getUser(), targetDomainObject, permission.toString());
    }

    @Override
    public boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, Object permission) {
        // almost the same implementation
    }
}

오브젝트 컬렉션을 저장하는 API를 구현하기 전까지는 모든 것이 정상적으로 동작합니다.이 서비스의 논리는 기존 엔티티를 갱신하거나 새로운 엔티티를 작성하는 것입니다.

@PreAuthorize("hasPermission(#products, 'WRITE')")
@RequestMapping(value = "/saveCollection", method = RequestMethod.POST)
public Collection<Product> save(@RequestBody Collection<Product> products) {
    return productService.save(products);
}

이 후 권한 서비스는 수집 개체를 처리하며 다음과 같이 표시됩니다.

PEMission 서비스:

public class PermissionService {

    public boolean isAuthorized(User user, Object targetDomainObject, String permission) {
        if (targetDomainObject instanceof TopAppEntity) {
            if (((TopAppEntity) targetDomainObject).getId() == null) {
                // check authorities and give response
            } else {
                // check ACL and give response
            }
        } else if(targetDomainObject instanceof Collection) {
            boolean isAuthorized = false;
            Collection targetDomainObjects = (Collection) targetDomainObject;
            for (Object targetObject : targetDomainObjects) {
                isAuthorized = isAuthorized(user, targetObject, permission);
                if (!isAuthorized) break;
            }
            return isAuthorized;
        }
    }
}

질문입니다.

다음을 사용하여 수집을 처리하는 방법@PreAuthorize("hasPermission(#object, '...')")좀 더 우아한 방법?Spring Security에서 수집을 처리하기 위한 몇 가지 구현이 있습니까?적어도 어떻게 하면PemissionService취급용Collections?

몇 가지 해결 방법이 있습니다.

1. 첫 번째는 내 것을 사용하는 것이다.MethodSecurityExpressionHandler그리고.MethodSecurityExpressionRoot.

작성CustomMethodSecurityExpressionRoot그리고 우리의 새로운 표현이 될 방법을 정의한다.Collection처리.연장될 것이다SecurityExpressionRoot기본식을 포함하려면:

public class CustomMethodSecurityExpressionRoot extends SecurityExpressionRoot implements MethodSecurityExpressionOperations {

    private final PermissionEvaluator permissionEvaluator;
    private final Authentication authentication;

    private Object filterObject;
    private Object returnObject;
    private Object target;

    public CustomMethodSecurityExpressionRoot(Authentication authentication, PermissionEvaluator permissionEvaluator) {
        super(authentication);
        this.authentication = authentication;
        this.permissionEvaluator = permissionEvaluator;
        super.setPermissionEvaluator(permissionEvaluator);
    }

    public boolean hasAccessToCollection(Collection<Object> collection, String permission) {
        for (Object object : collection) {
            if (!permissionEvaluator.hasPermission(authentication, object, permission))
                return false;
        }
        return true;
    }

    @Override
    public void setFilterObject(Object filterObject) {
        this.filterObject = filterObject;
    }

    @Override
    public Object getFilterObject() {
        return filterObject;
    }

    @Override
    public void setReturnObject(Object returnObject) {
        this.returnObject = returnObject;
    }

    @Override
    public Object getReturnObject() {
        return returnObject;
    }

    @Override
    public Object getThis() {
        return target;
    }
}

사용자 지정 식 핸들러를 만들고 주입합니다.CustomMethodSecurityExpressionRoot:

public class CustomMethodSecurityExpressionHandler extends DefaultMethodSecurityExpressionHandler {

    private final PermissionEvaluator permissionEvaluator;

    public CustomMethodSecurityExpressionHandler(PermissionEvaluator permissionEvaluator) {
        this.permissionEvaluator = permissionEvaluator;
        super.setPermissionEvaluator(permissionEvaluator);
    }

    @Override
    protected MethodSecurityExpressionOperations createSecurityExpressionRoot(
            Authentication authentication, MethodInvocation invocation) {
        CustomMethodSecurityExpressionRoot root =
                new CustomMethodSecurityExpressionRoot(authentication, permissionEvaluator);
        root.setTrustResolver(new AuthenticationTrustResolverImpl());
        root.setRoleHierarchy(getRoleHierarchy());
        return root;
    }
}

저도 주사했어요.SecurityPermissionEvaluator이 경우 커스텀 표현식과 디폴트 표현식의 단일 진입점이 됩니다.다른 옵션으로 주사하여 사용할 수 있습니다.PermissionService직접적으로.

방법 수준의 보안 설정:

@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {

    @Autowired
    private PermissionService permissionService;

    @Override
    protected MethodSecurityExpressionHandler createExpressionHandler() {
        PermissionEvaluator permissionEvaluator = new SecurityPermissionEvaluator(permissionService);
        return new CustomMethodSecurityExpressionHandler(permissionEvaluator);
    }
}

이제 에서 새로운 표현을 사용할 수 있습니다.RestController:

@PreAuthorize("hasAccessToCollection(#products, 'WRITE')")
@RequestMapping(value = "/saveCollection", method = RequestMethod.POST)
public Collection<Product> save(@RequestBody Collection<Product> products) {
    return productService.save(products);
}

그 결과 핸들링 컬렉션이 포함된 부품은PermissionService이 논리를 커스텀 표현에 적용했기 때문에 생략할 수 있습니다.

2. 두 번째 회피책은 SpEL을 사용하여 직접 메서드를 호출하는 것입니다.

지금 사용하고 있습니다.PermissionEvaluatoras Spring bean (여기서는 어떤 서비스도 사용할 수 있지만, 다시 단일 진입점을 선호합니다)

@Component
public class SecurityPermissionEvaluator implements PermissionEvaluator {

    @Autowired
    private PermissionService permissionService;

    @Override
    public boolean hasPermission(Authentication authentication, Object targetDomainObject, Object permission) {
        if (!(targetDomainObject instanceof TopAppEntity))
            throw new IllegalArgumentException();
        CustomUserDetails userDetails = (CustomUserDetails) authentication.getPrincipal();
        return permissionService.isAuthorized(userDetails.getUser(), targetDomainObject, permission.toString());
    }

    @Override
    public boolean hasPermission(Authentication authentication, Serializable targetId, String targetType, Object permission) {
        CustomUserDetails userDetails = (CustomUserDetails) authentication.getPrincipal();
        try {
            return permissionService.isAuthorized(userDetails.getUser(), targetId,
                    Class.forName(targetType), String.valueOf(permission));
        } catch (ClassNotFoundException e) {
            throw new IllegalArgumentException("No class found " + targetType);
        }
    }

    public boolean hasPermission(Authentication authentication, Collection<Object> targetDomainObjects, Object permission) {
        for (Object targetDomainObject : targetDomainObjects) {
            if (!hasPermission(authentication, targetDomainObject, permission))
                return false;
        }
        return true;
    }

}

메서드 보안 설정:

@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration {

    @Autowired
    private PermissionEvaluator permissionEvaluator;
    @Autowired
    private ApplicationContext applicationContext;

    @Override
    protected MethodSecurityExpressionHandler createExpressionHandler() {
        DefaultMethodSecurityExpressionHandler expressionHandler =
                new DefaultMethodSecurityExpressionHandler();
        expressionHandler.setPermissionEvaluator(permissionEvaluator);
        // Pay attention here, or Spring will not be able to resolve bean
        expressionHandler.setApplicationContext(applicationContext);
        return expressionHandler;
    }
}

표현식에서의 서비스 사용:

@PreAuthorize("@securityPermissionEvaluator.hasPermission(authentication, #products, 'WRITE')")
@RequestMapping(value = "/saveCollection", method = RequestMethod.POST)
public Collection<Product> save(@RequestBody Collection<Product> products) {
    return productService.save(products);
}

다른 이름이 지정되지 않은 경우 클래스 이름으로 기본적으로 생성되는 스프링 콩.

요약: 커스텀서비스를 직접 호출하거나 식으로서 등록하는 것에 근거해, 양쪽의 어프로치가 모두, 권한 확인 서비스에 송신되기 전에 수집 로직을 처리할 수 있기 때문에, 그 일부를 생략할 수 있습니다.

@Service
public class PermissionService {

    public boolean isAuthorized(User user, TopAppEntity domainEntity, String permission) {
        // removed instanceof checks and can operate on domainEntity directly
        if (domainEntity.getId() == null) {
            // check authorities and give response
        } else {
            // check ACL and give response
        }
    }
}

네, 똑똑한 방법이 있어요.내가 뭘 했는지 말해줄 수 있어.

@Component("MySecurityPermissionEvaluator ")
@Scope(value = "session")
public class PermissionService {

    @Autowired
    private PermissionEvaluator permissionEvaluator;

    public boolean myPermission(Object obj, String permission) {

        boolean isAuthorized = false;

        Authentication a = SecurityContextHolder.getContext()
                .getAuthentication();

        if (null == obj) {
            return isAuthorized;
        }

        if (a.getAuthorities().size() == 0) {
            logger.error("For this authenticated object, no authorities could be found !");
            return isAuthorized;
        } else {
            logger.error("Authorities found " + a.getAuthorities());
        }

        try {
            isAuthorized = myPermissionEval
                    .hasPermission(a, obj, permission);
        } catch (Exception e) {
            logger.error("exception while analysisng permissions");
        }

        return isAuthorized;
    }

하드 코드화된 권한을 사용하지 마십시오. 대신 이 방법을 사용하십시오.

import org.springframework.security.acls.domain.DefaultPermissionFactory;
public class MyPermissionFactory extends DefaultPermissionFactory {

    public MyPermissionFactory() {
        registerPublicPermissions(MyPermission.class);
    }

}

사용자 지정 권한을 만들려면

import org.springframework.security.acls.domain.BasePermission;

public class MyPermission extends BasePermission { //use this class for creating custom permissions
    private static Map<String, Integer> customPerMap = new HashMap<String, Integer>();
    static {
        customPerMap.put("READ", 1);
        customPerMap.put("WRITE", 2);
        customPerMap.put("DELETE", 4);
        customPerMap.put("PUT", 8);
    }

/**
 *Use the function while saving/ getting permission code 
**/
public static Integer getCode(String permName) {
        return customPerMap.get(permName.toUpperCase());
    }

관리자 사용자 또는 역할 계층에 따라 URL을 인증해야 할 경우 [Spring Authentication not Authorization]태그를 사용합니다.

나머지, 올바르게 사용하고 있습니다.@PreAuthorize 와 @PreFilter 는 모두 올바르고 요건에 맞게 사용되고 있습니다.

주석을 사용할 수 있습니다.

★★★★★★★★★★★★★★★★★.@PreFilter("hasPermission(filterTarget, '...')")허가 서비스

public class PermissionService() {

    public boolean isAuthorized(User user, Object targetDomainObject, String permission) {
        if (targetDomainObject instanceof TopAppEntity) {
            if (((TopAppEntity) targetDomainObject).getId() == null) {
                // check authorities and give response
            } else {
                // check ACL and give response
            }
        } 
    }
}

주의: 컨트롤러 메서드의 호출을 막지는 않습니다.빈 컬렉션만 가져옵니다.

기본 합니다.SecurityExpressionRoot권한 평가가 예를 들어 제품 소유자 분석에만 기반하는 경우 다음 식을 사용할 수 있습니다.

@GetMapping("")
@PostAuthorize("hasPermission(returnObject.![#this.owner],'ProductOwner','READ')")
public Collection<Product> getAllFiltering(<filters>) {...
@PostMapping("/collection")
@PreAuthorize("hasPermission(#products.![#this.owner],'ProductOwner','WRITE')")
public Collection<Product> save(@RequestBody Collection<Product> products) {...
@PutMapping("/collection")
@PreAuthorize("hasPermission(@productRepository.findByIds(#products.![#this.id]).![#this.owner],'ProductOwner','WRITE')")
public Collection<Product> update(@RequestBody Collection<Product> products) {...

, 「」, 「」가 됩니다.PermissionEvaluator수집을 처리할 수 있어야 합니다.해도 됩니다.PermissionEvaluator: 1개의 제품

@GetMapping("/{id}")
@PostAuthorize("hasPermission({ returnObject.owner },'ProductOwner','READ')")
public Product getById(@PathVariable int id) {...

'하다'를 하다.PermissionEvaluator어레이 또는 단일 값이 전달되었는지 분석합니다.

#products.![#this.owner] " - "6.5.17 수집 계획"을 참조하십시오.{ returnObject.owner }- https://docs.spring.io/spring/docs/3.0.x/reference/expressions.html 에서 "6.5.3 인라인 목록"을 참조하십시오.

언급URL : https://stackoverflow.com/questions/44251325/spring-security-haspermission-for-collectionobject

반응형