2013-06-23 28 views
4

我已經開始需要發明一種新的註釋類型,其中一個字段將是Spring表達式語言(aka SpEL)表達式字符串。如何在自定義java代碼中評估SpEL安全表達式?

後有點谷歌搜索和檢查現有的班,我已經想通了,評估表達的方式可能是這樣的一個(糾正我,如果我錯了,以任何方式):

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 

但在這裏是問題:最適合我的情況MethodSecurityExpressionRoot是包本地的。甚至有一個task about making it public in Spring Security JIRA這一年沒有得到開發者的關注。

而且,即使它不是包的地方,我還有哪裏獲得方法setTrustResolversetRoleHierarchySecurityExpressionRoot類,這似乎有需要爲它的正常運作setPermissionEvaluator對象弱的理解。

所以,我的問題是:你如何正確地得到正確的SecurityExpressionRoot-subclass實例以及如何使用所需的對象填充它?

+0

你爲什麼不使用'DefaultMethodSecurityExpressionHandler'?如果我可能會問,你的用例有什麼特別之處,你需要一個新的註釋? –

+0

感謝您指向'DefaultMethodSecurityExpressionHandler'的方向,但是您是否可以展示它應該用於的方式?什麼是'MethodInvocation'的東西,在那裏傳遞?你如何使用它? ---我的特殊用例是帶註釋控制器的建築物導航菜單;在某些情況下,我希望隱藏一些基於SpEL安全表達式的菜單項(例如,如果用戶未通過身份驗證或沒有「PROFILE」角色等,則不顯示「我的配置文件」菜單項); –

+0

可能的重複http://stackoverflow.com/questions/6632982/how-to-create-custom-methods-for-use-in-spring-security-expression-language-anno – Ralph

回答

0

我正在解決同樣的問題。我有一個菜單項列表。每個菜單項都包含一個安全表達式字符串(SpEl)。我試圖使用@PostFilter(「filterObject.securityExpression」),但我無法弄清楚如何評估一個SpEl字符串內的SpEl字符串。

所以我結束了自定義評估bean。受到org.thymeleaf.extras.springsecurity4.auth.AuthUtils的啓發。

評估程序使用與Web安全性過濾器相同的SecurityExpressionHandler。這意味着它有必要爲評估上下文提供請求和響應。但是,由於Spring將這些值注入到控制器方法中,所以這不應該很複雜。

計算器:

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

用法作爲控制器的方法:

@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

我設法實現了這個沒有任何新的註釋。您需要做的第一件事是將菜單項包裝在sec:authorize標籤中,其中sec命名空間來自spring security taglibs。我們使用:

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

其中${menuItem.permission}是當前menuItem對象(我們通過我們已經從服務器中檢索的菜單項循環)的permission領域。 SpEl hasRole()org.springframework.security.access.expression.SecurityExpressionOperations類中的spring實現。

雖然這不會給你帶來安全感,但它會讓gui變得更好。該服務器還需要與一些固定這樣的:

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

@PreAuthorize註釋也從春季的安全性,它從你的服務器上執行的方法,除非用戶具有給定角色停止客戶端。爲了使這項工作,我們必須執行org.springframework.security.cas.userdetails.AbstractCasAssertionUserDetailsService。大多數身份管理服務器都有類似的類。我們也必須實現org.jasig.services.persondir.support.ldap.LdapPersonAttributeDao,但我們也使用ldap。因人而異。