2017-04-23 188 views
3

我在我的春天啓動應用程序中使用Spring Security的,似乎Thymeleaf授權不能正常工作。使用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()"和授權總是被忽略,而不管用戶的角色。

我thymeleaf配置爲:

@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沒有在所有的工作,並sec命名空間總是被忽略Thymeleaf。
我的春天啓動的版本是1.5.2.RELEASE

可能是什麼原因?

UPDATE。SecurityConfigconfigure(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("https://stackoverflow.com/users/**").authenticated() 
      .and().authorizeRequests().antMatchers("https://stackoverflow.com/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"; 
} 
+1

據我所知的角色僅僅是一個特殊的'ROLE_'前綴的權威。我的角色沒有這個前綴,他們只是'USER','ADMIN'等:https://i.stack.imgur.com/jpBA1.png – DimaSan

+1

我聲明我的角色與'ROLE_'當我使用'hasRole',但表中的角色名稱本身沒有這個前綴。 – DimaSan

+0

@DimaSan:就是這一點。如果您使用'hasRole',則必須在數據庫中使用前綴。但你可以配置一個不同的前綴或使用'hasAuthority'(如你的答案)。 – dur

回答

4

解決任務的另一種方法是使用此語法檢查角色:

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

它不使用sec命名空間和a ctually並不需要在所有的thymeleaf-額外-springsecurity4依賴使用這個。

+1

謝謝您的建議,但結果是一樣的,Thymeleaf完全忽略了用戶的角​​色。順便說一句正確的是'日:如果= 「$ {#httpServletRequest.isUserInRole( 'SUPERUSER')}」'。 – DimaSan

+0

@DimaSan嗯,可能是版本之間的不匹配。 – holmis83

+0

你的意思是Thymelaf版本?我使用'spring-boot-starter-thymeleaf'依賴和Thymeleaf版本是2.1.5.RELEASE。 – DimaSan

0

事情嘗試:

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> 
+0

不幸的是,該行爲是相同的:當局'antMatchers'作品(使管理員可以訪問'.authorizeRequests()antMatchers。 ( 「/users/**").hasAnyAuthority("ADMIN」, 「SUPERUSER」)'頁面,在'SpringSecurityConfig'配置),但它們不與'秒Thymeleaf模板呈現:authorize'標籤。 另外角色從控制器打印爲我期望的,和管理具有'USER'和'ADMIN'角色。這必定會導致thymeleaf-extras-springsecurity4的錯誤。 無論如何非常感謝您的幫助。 – DimaSan

+0

好吧,總猜測,但我用我的POM片段更新了我的問題。我還沒有在Spring Security上做過小的升級,但是這種組合在沒有Boot的情況下適用於我。這一切都假設'sec'命名空間當然是在HTML文件的頂部定義的。 – bphilipnyc

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

希望能幫助別人,但這個問題仍然是開放的。