【问题标题】:Is there a Mustache equivalent to Thymeleafs sec:authorize tag?是否有等效于 Thymeleaf sec:authorize 标签的 Mustache?
【发布时间】:2019-01-08 11:14:18
【问题描述】:

我正在使用 ThymeLeaf 模板移植应用程序以使用 Mustache 模板,但我没有找到移植 ThymeLeaf 的 sec:authorize 标记的好方法。有没有办法像 Thymeleaf Spring Security extras 提供的那样在 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 模板中的角色的好方法?

【问题讨论】:

    标签: java spring-security thymeleaf mustache


    【解决方案1】:

    我刚刚为 mustache 做了一个启动器,以使用 spring security 的“基于表达式的访问控制”功能,请在此处查看代码: https://github.com/iceant/mustache-security-spring-boot-starter

    示例如下:

    {{#sec:hasRole('ADMIN')}}<li>ADMIN CONTENT</li>{{/sec:hasRole('ADMIN')}}
    {{#sec:hasRole('ADMIN') and hasRole('USER')}}<li>ADMIN & USER CONTENT</li>{{/sec:hasRole('ADMIN') and hasRole('USER')}}
    {{#sec:hasAnyRole('ADMIN', 'USER')}}<li>ADMIN OR USER CONTENT</li>{{/sec:hasAnyRole('ADMIN', 'USER')}}
    {{#sec:hasRole('USER')}}<li>USER CONTENT</li>{{/sec:hasRole('USER')}}
    {{#sec:isAnonymous()}}<li>isAnonymous</li>{{/sec:isAnonymous()}}{{^sec:isAnonymous()}}<li>isAnonymous=false</li>{{/sec:isAnonymous()}}
    {{#sec:isRememberMe()}}<li>isRememberMe</li>{{/sec:isRememberMe()}}{{^sec:isRememberMe()}}<li>isRememberMe=false</li>{{/sec:isRememberMe()}}
    {{#sec:isAuthenticated()}}<li>isAuthenticated</li>{{/sec:isAuthenticated()}}
    {{^sec:isAuthenticated()}}<li>isAuthenticated=false</li>{{/sec:isAuthenticated()}}
    {{#sec:isFullyAuthenticated()}}<li>isFullyAuthenticated</li>{{/sec:isFullyAuthenticated()}}
    {{^sec:isFullyAuthenticated()}}<li>isFullyAuthenticated=false</li>{{/sec:isFullyAuthenticated()}}
    {{#sec:principal}}<li>principal={{username}}{{/sec:principal}}
    {{#sec:authentication}}<li>authentication={{.}}{{/sec:authentication}}
    {{#sec:permitAll}}<li>permitAll</li>{{/sec:permitAll}}
    {{#sec:denyAll}}<li>denyAll</li>{{/sec:denyAll}}
    {{^sec:denyAll}}<li>denyAll=false</li>{{/sec:denyAll}}
    {{^sec:hasIpAddress('192.168.2.1')}}<li>hasIpAddress('192.168.2.1')=false</li>{{/sec:hasIpAddress('192.168.2.1')}}
    {{#sec:isMember(3)}}<li>isMember(3){{/sec:isMember(3)}}
    
    {{#sec:@webSecurity.check(authentication,request)}}<li>@webSecurity.check(authentication,request){{/sec:@webSecurity.check(authentication,request)}}
    

    【讨论】:

    【解决方案2】:

    我确定其中一个问题是安全上下文未在 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));
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2018-05-13
      • 2012-06-12
      • 1970-01-01
      • 1970-01-01
      • 2011-07-02
      • 2015-04-20
      • 1970-01-01
      • 2021-02-23
      • 2016-11-12
      相关资源
      最近更新 更多