Thymeleaf resolution with Spring Security not working

I am using Spring Security in my Spring Boot Application and it seems that Thymeleaf authorization is not working as expected.

I have a Thymeleaf template with the following code:

<div class="container">
    <div class="row" sec:authorize="isAuthenticated()">
        <h2 style="color:green">User is Logged In</h2>
        <p sec:authentication="principal.username">username</p>
    </div>

    <div class="row" sec:authorize="!isAuthenticated()">
        <h2 style="color:red">User is Logged Out</h2>
    </div>

    <div class="row" sec:authorize="hasRole('ROLE_SUPERUSER')">
        <h2>This will only be displayed if authenticated user has role ROLE_SUPERUSER.</h2>
    </div>

    <div class="row" sec:authorize="hasRole('ROLE_ADMIN')">
        <h2>This will only be displayed if authenticated user has role ROLE_ADMIN.</h2>
    </div>

    <div class="row" sec:authorize="hasRole('ROLE_USER')">
        <h2>This will only be displayed if authenticated user has role ROLE_USER.</h2>
    </div>

    <div th:if="${#authorization.expression('hasRole(''ROLE_ADMIN'')')}">
        This will only be displayed if authenticated user has role ROLE_ADMIN.
    </div>

    <div th:if="${#authorization.expr('hasRole(''ROLE_ADMIN'')')}">
        This will only be displayed if authenticated user has role ROLE_ADMIN.
    </div>
</div>

      

Examples are taken from: https://github.com/thymeleaf/thymeleaf-extras-springsecurity

However, the only displayed content is sec:authorize="isAuthenticated()"

and sec:authorize="!isAuthenticated()"

, and authorization is always ignored regardless of the user's role.

My thymeleaf config:

@Configuration
public class ThymeleafConfig {

    @Bean
    public TemplateResolver defaultTemplateResolver() {
        TemplateResolver resolver = new TemplateResolver();
        resolver.setResourceResolver(thymeleafResourceResolver());
        resolver.setPrefix("classpath:/templates/");
        resolver.setSuffix(".html");
        resolver.setTemplateMode("HTML5");
        resolver.setCharacterEncoding("UTF-8");
        resolver.setCacheable(true);
        return resolver;
    }

    @Bean
    public SpringResourceResourceResolver thymeleafResourceResolver() {
        return new SpringResourceResourceResolver();
    }

    @Bean
    public SpringTemplateEngine templateEngine(TemplateResolver templateResolver) {
        SpringTemplateEngine engine = new SpringTemplateEngine();
        engine.setTemplateResolver(templateResolver);
        engine.addDialect(new SpringSecurityDialect());
        engine.addDialect(new LayoutDialect());
        return engine;
    }

    @Bean
    public ThymeleafViewResolver thymeleafViewResolver(SpringTemplateEngine templateEngine) {
        ThymeleafViewResolver resolver = new ThymeleafViewResolver();
        resolver.setTemplateEngine(templateEngine);
        resolver.setCharacterEncoding("UTF-8");
        resolver.setContentType("text/html");
        resolver.setOrder(Ordered.LOWEST_PRECEDENCE - 5);
        return resolver;
    }

}

      

And I am using the following dependency for thymeleaf-extras-springsecurity4

:

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

      

The version 3.0.2.RELEASE

doesn't work at all and the namespace was sec

always ignored by Thymeleaf.
My Spring bootable version 1.5.2.RELEASE

.

What could be the reason?

UPDATE. Method configure(HttpSecurity http)

c SecurityConfig

looks like this:

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.csrf().ignoringAntMatchers("/h2-console").disable()
            .authorizeRequests()
                .antMatchers("/webjars/**", "/static/**", "/images/**", "/**/favicon.ico").permitAll()
                .antMatchers("/heat/**", "/power/**", "/water/**").permitAll()

            // start allowing h2-console
                .antMatchers("/h2-console/**").permitAll();
            http.csrf().disable();
            http.headers().frameOptions().disable()
            // end allowing h2-console

            .and().authorizeRequests().antMatchers("/info").permitAll()
            .and().authorizeRequests().antMatchers("/users/**").authenticated()
            .and().authorizeRequests().antMatchers("/users/**").hasAnyAuthority("ADMIN", "SUPERUSER")

            .and().formLogin()
                    .loginPage("/login")
                    .permitAll()
            .and()
                    .logout()
                    .permitAll()
                    .deleteCookies("remove")
                    .logoutUrl("/logout")
                    .logoutSuccessUrl("/")
                    .invalidateHttpSession(true)

            .and().exceptionHandling().accessDeniedPage("/access_denied");
}

      

and the mapping from is IndexController

pretty simple, it just returns a template login

:

@RequestMapping("/login")
public String loginForm() {
    return "login";
}

      

+3


source to share


3 answers


An alternative way to solve the problem is to use this syntax to check the role:

<div class="row" th:if="${#request.isUserInRole('SUPERUSER')}">
    <h2>This will only be displayed if authenticated user has role ROLE_SUPERUSER.</h2>
</div>

      



It doesn't use a namespace sec

, and in fact the dependency on thymeleaf-extras-springsecurity4 is not needed at all to use this.

+6


source


After many attempts with various configurations, I found a workaround. In this case, the attribute sec:authorize="hasAuthority('ADMIN')"

works:

<div class="row" sec:authorize="hasRole('ROLE_ADMIN')">
    <div class="col-md-10 col-md-offset-2">
        <h2>User Has Role Admin</h2>
    </div>
</div>
<div class="row" sec:authorize="hasAuthority('ADMIN')">
    <div class="col-md-10 col-md-offset-2">
        <h2>User Has Authority Admin</h2>
    </div>
</div>

      

and the User Has Authority Admin

heading title on the page.



Still don't know why the attribute is sec:authorize="hasRole('ROLE_ADMIN')"

n't working, as suggested as an example on the thymeleaf-extras-springsecurity

GitHub page : https://github.com/thymeleaf/thymeleaf-extras-springsecurity#using-the-attributes

Hope this helps someone, although the question is still open.

+1


source


Things to Try:

1) Annotate your config with @EnableWebMvc

.

2) Replace ROLE_ADMIN

only ADMIN

(and others accordingly).

3) In your controller, type this to see the current roles:

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

Set<String> roles = authentication.getAuthorities().stream()
     .map(r -> r.getAuthority()).collect(Collectors.toSet());

System.out.println(roles);

      

If that doesn't work for you, try getUserPrincipal()

out HttpServletRequest

.

Except for this:

I am including MVC config so you can try the latest Thymeleaf and Spring security. There are several additional configurations, so you can remove what is not relevant to your project.

@Configuration
@EnableWebMvc
public class WebMvcConfig extends WebMvcConfigurerAdapter {

    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configuration) {

        configuration.enable();
    }

    @Bean
    public ThymeleafViewResolver viewResolver() {

        ThymeleafViewResolver resolver = new ThymeleafViewResolver();
        resolver.setOrder(1);
        resolver.setCharacterEncoding(StandardCharsets.UTF_8.name());
        resolver.setTemplateEngine(templateEngine());
        return resolver;
    }

    @Bean
    public TemplateEngine templateEngine() {

        Set<ITemplateResolver> templateResolvers = new LinkedHashSet<>(1);
        templateResolvers.add(webTemplateResolver());

        SpringTemplateEngine templateEngine = new SpringTemplateEngine();
        templateEngine.setTemplateResolvers(templateResolvers);
        Set<IDialect> dialects = new LinkedHashSet<>(2);
        dialects.add(new SpringSecurityDialect());
        dialects.add(new Java8TimeDialect());
        templateEngine.setAdditionalDialects(dialects);
        return templateEngine;
    }

    @Bean
    public ITemplateResolver webTemplateResolver() {

        SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver();
        resolver.setPrefix("/WEB-INF/thymeleaf/");
        resolver.setTemplateMode(TemplateMode.HTML);
        resolver.setCharacterEncoding(StandardCharsets.UTF_8.name());
        resolver.setSuffix(".html");
        resolver.setCacheable(false);
        resolver.setOrder(2);
        return resolver;
    }

    @Bean
    public ViewResolver tilesViewResolver() {

        UrlBasedViewResolver viewResolver = new UrlBasedViewResolver();
        viewResolver.setViewClass(TilesView.class);
        viewResolver.setOrder(0);
        return viewResolver;
    }

    @Bean
    public TilesConfigurer tilesConfigurer() {

        TilesConfigurer configurer = new TilesConfigurer();
        configurer.setDefinitions("/WEB-INF/**/views.xml");
        return configurer;
    }

    @Bean
    public LocalValidatorFactoryBean validator() {

        return new LocalValidatorFactoryBean();
    }

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {

        registry.addViewController("/403");
        registry.addViewController("/404");
        registry.addViewController("/about");
        //edited for brevity
    }

    @Bean
    public ReloadableResourceBundleMessageSource messageSource() {

        ReloadableResourceBundleMessageSource source = new ReloadableResourceBundleMessageSource();
        source.setBasename("classpath:messages");
        source.setDefaultEncoding(StandardCharsets.UTF_8.name());
        return source;
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {

        registry.addInterceptor(localeChangeInterceptor());
        registry.addInterceptor(themeChangeInterceptor());
        registry.addInterceptor(deviceResolverHandlerInterceptor());
        super.addInterceptors(registry);
    }

    @Bean
    public HandlerInterceptor localeChangeInterceptor() {

        LocaleChangeInterceptor interceptor = new LocaleChangeInterceptor();
        interceptor.setParamName("lang");
        return interceptor;
    }

    @Bean
    public HandlerInterceptor themeChangeInterceptor() {

        ThemeChangeInterceptor interceptor = new ThemeChangeInterceptor();
        interceptor.setParamName("theme");
        return interceptor;
    }

    @Bean
    public ResourceBundleThemeSource themeSource() {

        ResourceBundleThemeSource themeSource = new ResourceBundleThemeSource();
        themeSource.setBasenamePrefix("theme-");
        return themeSource;
    }

    @Bean
    public PersistedThemeResolver themeResolver() {

        PersistedThemeResolver resolver = new PersistedThemeResolver();
        resolver.setDefaultThemeName("default");
        return resolver;
    }

    @Bean
    public HandlerInterceptor deviceResolverHandlerInterceptor() {

        return new DeviceResolverHandlerInterceptor();
    }

    @Bean
    public CookieLocaleResolver localeResolver() {

        CookieLocaleResolver resolver = new CookieLocaleResolver();
        resolver.setDefaultLocale(Locale.US);
        return resolver;
    }

    //removed custom bean declaration

    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {

        argumentResolvers.add(new ServletWebArgumentResolverAdapter(new DeviceWebArgumentResolver()));
        super.addArgumentResolvers(argumentResolvers);
    }

    @Bean
    public Executor taskExecutor() {

        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(100);
        executor.initialize();
        return executor;
    }
}

      

An excerpt from the working pump:

    <dependency>
        <groupId>org.thymeleaf</groupId>
        <artifactId>thymeleaf</artifactId>
        <version>3.0.2.RELEASE</version>
        <exclusions>
            <exclusion>
                <groupId>javassist</groupId>
                <artifactId>javassist</artifactId>
            </exclusion>
        </exclusions>
    </dependency>

    <dependency>
        <groupId>org.thymeleaf</groupId>
        <artifactId>thymeleaf-spring4</artifactId>
        <version>3.0.2.RELEASE</version>
    </dependency>

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

      

0


source







All Articles