是否有与Thymeleafs sec:authorize标签等效的小胡子?

时间:2018-08-01 00:40:42

标签: java spring-security thymeleaf mustache

我正在努力移植带有ThymeLeaf模板的应用程序以使用Mustache模板,但是我没有找到移植ThymeLeaf的sec:authorize标签的好方法。是否有办法像Thymeleaf Spring Security附加功能所提供的那样在Mustache中获取SecurityContext信息?

<dependency>
    <groupId>org.thymeleaf.extras</groupId>
    <artifactId>thymeleaf-extras-springsecurity4</artifactId>
</dependency>

我以为可以使用@ControllerAdvice来注入模型属性,但是SecurityContextHolder.getContents().getAuthentication()为空。但是我可以检查HttpServletRequest对象上的角色。这似乎是一个时间问题,因为在主@Controller中我可以访问Authentication对象。我可以提取主体名称,而isAuthenticated()返回正确的值。这是我在@ControllerAdvice上尝试过的方法:

package com.example;

import java.util.Map.Entry;
import java.util.stream.Stream;

import javax.servlet.http.HttpServletRequest;

import org.example.security.entity.UserRole;
import org.example.security.entity.UserRole.Role;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ModelAttribute;

@ControllerAdvice
public class TemplateAdvice {

    private static String ANONYMOUS_USER = "anonymousUser";

    private Authentication authentication;

    private Object principal;

    private String username;

    private boolean isAuthenticated;

    private boolean isAnonymous;

    private HttpServletRequest request;

    public TemplateAdvice() {
        this.authentication = SecurityContextHolder.getContext()
            .getAuthentication();
        if (this.authentication != null) {
            this.principal = this.authentication.getPrincipal();
            this.username = this.authentication.getName();
        }
        this.isAnonymous = this.principal == null 
            || this.username.equals(ANONYMOUS_USER);
    }

    @ModelAttribute
    public void addDefaultAttributes(HttpServletRequest request, 
            Model model) {
        this.request = request;

        model.addAttribute("isAuthenticated", this.authentication.isAuthenticated());
        model.addAttribute("isAnonymous", this.isAnonymous);
        model.addAttribute("username", this.username);
        model.addAttribute("isAuthenticated", hasAnyRole()); // hack
        model.addAttribute("isAdminOrSuper", 
            hasRole(UserRole.Role.ADMIN) 
                || hasRole(UserRole.Role.SUPER)); // this works
    }

    private boolean hasRole(Role role) {
        return this.request.isUserInRole(role.name());
    }

    private boolean hasAnyRole() {
        return Stream.of(UserRole.Role.values())
            .anyMatch(role -> hasRole(role));
    }
}

我正在通过使用抽出contextPath_csrf.token

spring.mustache.expose-request-attributes=true
spring.mustache.request-context-attribute=req

,希望某些安全上下文可以类似地公开。

我在Google上找不到很多示例。有没有一种很好的方法来检查用户是否已通过身份验证以及他们在Mustache模板中所扮演的角色?

2 个答案:

答案 0 :(得分:0)

我确定问题之一是安全上下文未在TemplateAdvice构造函数中解析。将其移至addDefaultAttributes后,一切正常。这是我现在想到的:

package com.example.authentication.server.controller;

import javax.servlet.http.HttpServletRequest;

import com.example.authentication.server.security.SecurityHelper;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ModelAttribute;

@ControllerAdvice
public class TemplateAdvice {

    private SecurityHelper securityHelper;

    @ModelAttribute
    public void addDefaultAttributes(HttpServletRequest request, Model model) {
        securityHelper = new SecurityHelper(SecurityContextHolder.getContext());

        model.addAttribute("isLoggedIn", securityHelper.isAuthenticated() 
            && !securityHelper.isAnonymous());
        model.addAttribute("username", securityHelper.username());
        model.addAttribute("isAdminOrSuper", securityHelper.isAdminOrSuper());
    }
}

以及相应的SecurityHelper类:

package com.example.authentication.server.security;

import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

import org.springframework.security.authentication.AnonymousAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContext;

public class SecurityHelper {

    public static enum Role {
        ROLE_USER, ROLE_ADMIN;
    }

    private Collection<? extends GrantedAuthority> authorities = 
        Collections.emptyList();

    private Authentication authentication;

    public SecurityHelper(SecurityContext context) {
        authentication = context.getAuthentication();
        if (authentication != null) {
            authorities = authentication.getAuthorities();
        }
    }

    public boolean isAuthenticated() {
        return authentication == null ? false : authentication.isAuthenticated();
    }

    public boolean isAnonymous() {
        return authentication == null ? true : 
            authentication instanceof AnonymousAuthenticationToken;
    }

    public String username() {
        return authentication == null ? "" : authentication.getName();
    }

    public boolean isAdminOrSuper() {
        return hasAnyRole(Arrays.asList(Role.ROLE_ADMIN, Role.ROLE_SUPER));
    }

    /**
    * Checks if user contains the given role.
    * 
    * @param role
    *            A user role.
    * @return True if the user contains the role.
    */
    public boolean hasRole(Role role) {
        return authorities == null ? false
                : authorities.stream().anyMatch(authority -> 
                    authority.getAuthority().equals(role.name()));
    }

    /**
    * Checks if a user contains at least one of the roles.
    * 
    * @param roles
    *            A list of user roles.
    * @return True if the user contains one of the roles.
    */
    public boolean hasAnyRole(List<Role> roles) {
        return roles.stream().anyMatch(role -> hasRole(role));
    }
}

答案 1 :(得分:0)

我刚刚制作了一个小胡子,即可使用Spring Security的“基于表达式的访问控制”功能,请在此处检查代码: https://github.com/iceant/mustache-security-spring-boot-starter

以下是示例:

Point