使用 Spring Security 的 Thymeleaf 授权不起作用[重复]
Posted
技术标签:
【中文标题】使用 Spring Security 的 Thymeleaf 授权不起作用[重复]【英文标题】:Thymeleaf authorization with Spring Security doesn't work [duplicate] 【发布时间】:2017-09-20 11:37:55 【问题描述】:我在我的 Spring Boot 应用程序中使用 Spring Security,Thymeleaf 授权似乎无法正常工作。
我有带有以下代码的 Thymeleaf 模板:
<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>
示例来自:https://github.com/thymeleaf/thymeleaf-extras-springsecurity
但是显示的唯一内容是sec:authorize="isAuthenticated()"
和sec:authorize="!isAuthenticated()"
,并且无论用户的角色如何,授权总是被忽略。
我的百里香配置是:
@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;
我对@987654322@ 使用以下依赖项:
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity4</artifactId>
<version>2.1.3.RELEASE</version>
</dependency>
版本 3.0.2.RELEASE
根本不起作用,sec
命名空间总是被 Thymeleaf 忽略。
我的 Spring Boot 版本是1.5.2.RELEASE
。
可能是什么原因?
更新。SecurityConfig
中的 configure(HttpSecurity http)
方法如下所示:
@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");
IndexController
的映射非常简单,它只返回 login
模板:
@RequestMapping("/login")
public String loginForm()
return "login";
【问题讨论】:
据我所知,角色只是一个带有特殊ROLE_
前缀的权限。我的角色没有这个前缀,它们只是USER
、ADMIN
等等:i.stack.imgur.com/jpBA1.png
当我使用hasRole
时,我会在我的角色前面加上ROLE_
,但表中的角色名称本身没有这个前缀。
@DimaSan:这就是重点。如果使用hasRole
,则必须在数据库中使用前缀。但是您可以配置不同的前缀或使用hasAuthority
(如您的回答)。
是的,您是对的,现在我可以确认它可以按照您的描述工作。那么,让角色名称带有或不带有 ROLE_
前缀的最佳做法是什么?
我只使用带前缀的角色名,因为我不喜欢太多的自定义配置。我尽量使用默认值。但是 Spring 开发人员会说,两者皆有可能是你的决定。
【参考方案1】:
解决任务的另一种方法是使用以下语法检查角色:
<div class="row" th:if="$#request.isUserInRole('SUPERUSER')">
<h2>This will only be displayed if authenticated user has role ROLE_SUPERUSER.</h2>
</div>
它不使用 sec
命名空间,实际上根本不需要 thymeleaf-extras-springsecurity4 依赖项来使用它。
【讨论】:
谢谢你的建议,但结果是一样的,Thymeleaf 完全忽略了用户的角色。顺便说一句,右边是th:if="$#httpServletRequest.isUserInRole('SUPERUSER')"
。
@DimaSan 嗯,可能是版本不匹配。
你是说 Thymelaf 版本吗?我使用 spring-boot-starter-thymeleaf
依赖和 Thymeleaf 版本有 2.1.5.RELEASE。
感谢您的回答。无论我尝试什么,我都无法获得 sec:authorize 工作。它总是返回 true。不过你回答得很完美。
这实际上对我的 spring mvc 和 spring security 4.2.3 有效【参考方案2】:
要尝试的事情:
1) 使用 @EnableWebMvc
注释您的配置。
2) 仅将 ROLE_ADMIN
替换为 ADMIN
(以及其他相应的)。
3) 在您的控制器中,打印此内容以查看您当前的角色:
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
Set<String> roles = authentication.getAuthorities().stream()
.map(r -> r.getAuthority()).collect(Collectors.toSet());
System.out.println(roles);
如果这对您不起作用,不妨试试 getUserPrincipal()
和 HttpServletRequest
。
简短:
我包含了我的 MVC 配置,以便您可以尝试最新的 Thymeleaf 和 Spring Security 版本。那里有一些额外的配置,因此您可以删除与您的项目无关的内容。
@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;
工作 pom 摘录:
<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>
【讨论】:
不幸的是,使用您的WebMvcConfig
配置,行为是相同的:权限与antMatchers
一起使用(以便管理员可以访问.authorizeRequests().antMatchers("/users/**").hasAnyAuthority("ADMIN", "SUPERUSER")
页面,在SpringSecurityConfig
中配置)但它们没有呈现在带有 sec:authorize
标签的 Thymeleaf 模板中。正如我所料,角色也是从控制器打印的,管理员有USER
和ADMIN
角色。 thymeleaf-extras-springsecurity4 肯定有问题。无论如何,非常感谢您的帮助。
好吧,完全是猜测,但我用我的 POM 中的 sn-p 更新了我的问题。我还没有对 Spring Security 进行小升级,但是这种组合在没有 Boot 的情况下对我有用。这一切都假设sec
命名空间当然是在 HTML 文件的顶部定义的。【参考方案3】:
经过多次尝试不同配置后,我找到了一种解决方法。在这种情况下,sec:authorize="hasAuthority('ADMIN')"
属性有效:
<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>
和User Has Authority Admin
标头呈现在页面上。
仍然不知道为什么sec:authorize="hasRole('ROLE_ADMIN')"
属性不起作用,正如thymeleaf-extras-springsecurity
GitHub 页面上的示例所建议的那样:
https://github.com/thymeleaf/thymeleaf-extras-springsecurity#using-the-attributes
希望这可以帮助某人,尽管问题仍然悬而未决。
【讨论】:
以上是关于使用 Spring Security 的 Thymeleaf 授权不起作用[重复]的主要内容,如果未能解决你的问题,请参考以下文章
带有 Cordova 默认应用程序 XML 错误的 Eclipse THyM
Spring Framework,Spring Security - 可以在没有 Spring Framework 的情况下使用 Spring Security?
仅使用 Spring-Security(或)Spring 进行授权
在使用 Oauth、SAML 和 spring-security 的多租户的情况下从 spring-security.xml 中获取错误