How to evaluate SpEL Security expression in custom java code?

I need to come up with a new type of annotation, one of the fields of which will be the string of the Spring Expression Language (aka SpEL) expression.

After doing a little googling search and exploring existing classes, I realized that the way to evaluate an expression might be similar to this (correct me if I'm wrong):

ExpressionParser parser = new SpelExpressionParser(); Expression exp = parser.parseExpression("isAnonymous()"); // well, this is only an example SecurityExpressionRoot context = ... obtaining the instance of subclass of SecurityExpressionRoot ... System.out.println(exp.getValue(context)); // just an example 

But here is the problem: the MethodSecurityExpressionRoot most suitable for my case is a local package. There is even the task of publishing in Spring Security JIRA , which has not received any attention from developers for a year.

And even if it wasn’t local with the package, I still have a poor understanding of where to get the objects for the setTrustResolver , setRoleHierarchy and setPermissionEvaluator of the SecurityExpressionRoot class, which apparently are necessary for this to function properly.

So my question is: how to get the correct instance of SecurityExpressionRoot -subclass and how to fill it with the necessary objects?

+4
source share
2 answers

I solve the same problem. I have a list of menu items. Each menu item contains a security expression string (SpEl). I tried using @PostFilter ("filterObject.securityExpression"), but I could not figure out how to evaluate the SpEl line inside the SpEl line.

So, I got a custom bean appraiser. Strongly inspired by org.thymeleaf.extras.springsecurity4.auth.AuthUtils

The evaluator uses the same SecurityExpressionHandler as the web security filters. This means the need to provide a request and response for the evaluation context. But this does not have to be complicated, as Spring introduces these values ​​into controller methods.

appraiser:

 @Component public class WebSecurityExpressionEvaluator { private static final FilterChain EMPTY_CHAIN = (request, response) -> { throw new UnsupportedOperationException(); }; private final List<SecurityExpressionHandler> securityExpressionHandlers; public WebSecurityExpressionEvaluator(List<SecurityExpressionHandler> securityExpressionHandlers) { this.securityExpressionHandlers = securityExpressionHandlers; } public boolean evaluate(String securityExpression, HttpServletRequest request, HttpServletResponse response) { SecurityExpressionHandler handler = getFilterSecurityHandler(); Expression expression = handler.getExpressionParser().parseExpression(securityExpression); EvaluationContext evaluationContext = createEvaluationContext(handler, request, response); return ExpressionUtils.evaluateAsBoolean(expression, evaluationContext); } @SuppressWarnings("unchecked") private EvaluationContext createEvaluationContext(SecurityExpressionHandler handler, HttpServletRequest request, HttpServletResponse response) { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); FilterInvocation filterInvocation = new FilterInvocation(request, response, EMPTY_CHAIN); return handler.createEvaluationContext(authentication, filterInvocation); } private SecurityExpressionHandler getFilterSecurityHandler() { return securityExpressionHandlers.stream() .filter(handler -> FilterInvocation.class.equals(GenericTypeResolver.resolveTypeArgument(handler.getClass(), SecurityExpressionHandler.class))) .findAny() .orElseThrow(() -> new IllegalStateException("No filter invocation security expression handler has been found! Handlers: " + securityExpressionHandlers.size())); } } 

Using as a controller method:

 @ModelAttribute("adminMenuItems") public List<AdminMenuItem> getMenuItems(HttpServletRequest request, HttpServletResponse response) { List<AdminMenuItem> menuItems = ... return menuItems.stream().filter(item -> evaluator.evaluate(item.getSecurityExpression(), request, response)).collect(toList()); } 
0
source

I managed to achieve just that without any new annotations. The first thing you need to do is wrap the menu item in the sec: authorize tag, where the sec namespace is from the spring security taglib. We use:

 <sec:authorize access="hasRole('${menuItem.permission}')"></sec:authorzie> 

where ${menuItem.permission} is the permission field of the current menuItem object (we menuItem over the menu items that we extracted from the server). SpEl hasRole() is implemented by spring in the class org.springframework.security.access.expression.SecurityExpressionOperations .

This will not give you security, but it will just make gui enjoyable. The server should also be protected with something like this:

 @PreAuthorize("hasRole('...')") 

The @PreAuthorize also applies to spring security, and it does not allow the client to execute the method on your server, unless the user has this role. To do this, we had to implement org.springframework.security.cas.userdetails.AbstractCasAssertionUserDetailsService . A similar class exists for most identity management servers. We also had to implement org.jasig.services.persondir.support.ldap.LdapPersonAttributeDao , but we also use ldap. YMMV.

0
source

Source: https://habr.com/ru/post/1487750/


All Articles