我在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;
}
}
我对thymeleaf-extras-springsecurity4
使用以下依赖项:
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity4</artifactId>
<version>2.1.3.RELEASE</version>
</dependency>
版本3.0.2.RELEASE
根本不起作用,Thymeleaf总是忽略sec
名称空间。
我的Spring Boot版本是1.5.2.RELEASE
。
可能是什么原因?
更新。 configure(HttpSecurity http)
中的SecurityConfig
方法如下所示:
@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";
}
答案 0 :(得分:9)
解决任务的另一种方法是使用此语法来检查角色:
<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依赖。
答案 1 :(得分:1)
Things to try:
1) Annotate your configuration with @EnableWebMvc
.
2) Replace ROLE_ADMIN
with just ADMIN
(and the others correspondingly too).
3) In your controller, print this to see your 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, perhaps try getUserPrincipal()
from HttpServletRequest
.
Short of that:
I am including my MVC config so that you can try the latest Thymeleaf and Spring Security versions. There are some extra configurations in there, so you can remove what's 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;
}
}
Working pom excerpt:
<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>
答案 2 :(得分:1)
经过大量不同配置的尝试后,我找到了解决方法。在这种情况下,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
希望这可以帮助某人,尽管问题仍然存在。