【问题标题】:Spring security - @PreAuthorize not workingSpring 安全性 - @PreAuthorize 不起作用
【发布时间】:2015-06-20 23:57:21
【问题描述】:

我在使用 @PreAuthorize 注释时遇到问题。即使我的用户不拥有所要求的角色,我的安全方法也会被执行。

我的控制器:

@Controller
@RequestMapping("/stats/distributions")
public class DistributionStatsController {

    @PreAuthorize("hasAnyAuthority('AK_LOCAL_DIST_INT', 'AK_ADMIN')")
    @RequestMapping(method = RequestMethod.POST, consumes = "application/json; charset=utf-8", 
        produces = "application/json; charset=utf-8")
    public @ResponseBody List<DistributionStatsResource> filter(@RequestBody DistributionStatsResource resource,  
           @RequestParam(required = false, value = "documentId") Long documentId, 
           @RequestParam(required = false, value = "distStatus") EnumDistributionStatus distributionStatus, 
           Pageable pageable, HttpServletRequest request) {
    }
}

这是我的 spring 安全配置:

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    /** Defines the AuthenticationManager/providers. */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.authenticationProvider(preAuthenticatedAuthenticationProvider());
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/css/**", "/font/**", "/icones/**", "/img/**");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // TODO Configure HTTP URLs and filters.
        http.authorizeRequests().antMatchers("/views/access401.html").permitAll().antMatchers("/views/admin/agent.html").hasAuthority("AK_ADMIN")
        .antMatchers("/views/admin/agentDetail.html").hasAuthority("AK_ADMIN").antMatchers("/views/admin/businesses.html")
        .hasAuthority("AK_ADMIN").antMatchers("/views/admin/distributors.html").hasAuthority("AK_ADMIN")
        .antMatchers("/views/admin/distributionReportList.html").hasAuthority("AK_ADMIN")
        .antMatchers("/views/documentEdition/documentDetail.html").hasAnyAuthority("AK_CENTRAL_DIST", "AK_LOCAL_DIST_INT", "AK_ADMIN")

        .antMatchers("/views/home/home.html").fullyAuthenticated().antMatchers("/views/distribution/distribution.html")
        .hasAnyAuthority("AK_LOCAL_DIST_INT", "AK_ADMIN").antMatchers("/views/distribution/distributionEdit.html")
        .hasAnyAuthority("AK_LOCAL_DIST_INT", "AK_ADMIN").antMatchers("/views/admin/types.html").hasAuthority("AK_ADMIN").and()
            .exceptionHandling().authenticationEntryPoint(unauthorizedEntryPoint()).and().addFilter(habileFilter()).csrf().disable(); // Disable CSRF
        // protection.
    }

    /** Gives an alias to the authenticationManager. */
    @Override
    @Bean(name = "authenticationManager")
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    /** A unauthorized entry point. */
    @Bean
    public AuthenticationEntryPoint unauthorizedEntryPoint() {
        return new ForbiddenEntryPoint();
    }

    /** The user details service used by the PreAuthenticatedAuthenticationProvider. */
    @Bean
    public AuthenticationUserDetailsService<PreAuthenticatedAuthenticationToken> myAuthenticationUserDetailsService() {
        return new NgwisAuthenticationUserDetailsService();
    }

    /** The PreAuthenticatedAuthenticationProvider. */
    @Bean
    public PreAuthenticatedAuthenticationProvider preAuthenticatedAuthenticationProvider() {
        PreAuthenticatedAuthenticationProvider pro = new PreAuthenticatedAuthenticationProvider();
        pro.setPreAuthenticatedUserDetailsService(myAuthenticationUserDetailsService());
        return pro;
    }

    // ---- Filters.

    /** Builds an Habile filter.
     *
     * @return the habile filter. */
    @Bean
    public RequestHeaderAuthenticationFilter habileFilter() throws Exception {
        NgwisRequestHeaderAuthenticationFilter filter = new NgwisRequestHeaderAuthenticationFilter();
        filter.setPrincipalRequestHeader("SM_USER");
        filter.setCredentialsRequestHeader(NgwisRequestHeaderAuthenticationFilter.HABILE_FILTER_NAME);
        filter.setAuthenticationManager(authenticationManager());
        return filter;
    }
}

(这个类在我的基础配置类中被引用)

我的RequestHeaderAuthenticationFilter 班级:

public class NgwisRequestHeaderAuthenticationFilter extends RequestHeaderAuthenticationFilter {

    public static final String HABILE_FILTER_NAME = "HABILE";

    /** Pour mise à disposition des informations de sécurité */
    public static final String BEAN_SECURITIES = "com.airfrance.springsecurity.securities";

    private static final org.slf4j.Logger logger = LoggerFactory.getLogger(NgwisRequestHeaderAuthenticationFilter.class);

    // AK de l'utilisateur en fonction de ses profils
    private UserAccessKeys userAccessKeys = null;

    // Pour passer l'info au niveau de la config de spring security
    private String credentialsRequestHeader;

    @Inject
    private IAgentService agentService;

    @Inject
    private DozerBeanMapper mapper;

    /** Credentials aren't usually applicable, but if a {@code credentialsRequestHeader} is set, this will be read and used as
     * the credentials value. Otherwise a dummy value will be used. */
    @Override
    protected Object getPreAuthenticatedCredentials(HttpServletRequest request) {
        Collection<GrantedAuthority> tmp = new ArrayList<GrantedAuthority>();
        User user = new User(request.getRemoteUser().toUpperCase(), "none", false, false, false, false, tmp);
        if (credentialsRequestHeader != null) {
            if (credentialsRequestHeader.equalsIgnoreCase("HABILE")) {
                try {
                    LdapBean ldBean = LdapBeanAccessor.getLdapBean(request);
                    if (ldBean != null) {
                        userAccessKeys = new UserAccessKeys(request, ldBean, agentService, mapper);
                        request.getSession().setAttribute(BEAN_SECURITIES, userAccessKeys);
                        List<String> auths = new ArrayList<String>();
                        for (GrantedAuthority auth : userAccessKeys.getAuthorities()) {
                            auths.add(auth.getAuthority());
                        }
                        logger.debug("User {} connected with authorities {}", userAccessKeys.getLogin(), StringUtils.join(auths, ", "));
                        user = new User(request.getRemoteUser().toUpperCase(), "none", true, true, true, true, userAccessKeys.getAuthorities());
                    }
                } catch (NoLdapBeanInSessionException e) {
                    logger.error("Erreur lors de la connexion de {}", request.getRemoteUser().toUpperCase(), e);
                } catch (NotProtectedGetLdapException e) {
                    logger.error("Erreur technique ", e);
                }
                if (userAccessKeys.getAgent() != null) {
                    return user;
                } else {
                    return null;
                }
            } else {
                return request.getHeader(credentialsRequestHeader);
            }
        }

        return "N/A";
    }

    @Override
    public void setCredentialsRequestHeader(String credentialsRequestHeader) {
        Assert.hasText(credentialsRequestHeader, "credentialsRequestHeader must not be empty or null");
        this.credentialsRequestHeader = credentialsRequestHeader;
    }
}

我在这个类中检查了我们获得了登录用户的权限。一切似乎都很好。

当我使用仅具有AK_CONSULT 角色的用户运行此代码时,将执行该方法并且不会触发503 ERROR

感谢您的帮助。

【问题讨论】:

  • 使用 XML 配置 Spring 时,必须在 Spring MVC 上下文而不是 Spring Security 上启用“pre-post”。这可能是您遇到的同一个问题(我不是使用注释来配置 Spring 的忠实粉丝)吗?
  • 你可以看看这里,我认为这也是一个问题并且可以提供帮助:stackoverflow.com/questions/29275890/…
  • 看来是我的问题。但是为了解决这个问题,他在服务层上设置了注释,而不是在控制器上(我想要的)。正如他们在评论中所说,网络上有很多此注释位于控制器中的示例。

标签: java spring spring-security


【解决方案1】:

我的同事发现了诀窍。 @EnableGlobalMethodSecurity(prePostEnabled = true) 注解必须spring-security 配置类中,但在 Servlet 配置类中。

@Configuration
@EnableWebMvc
@EnableSpringDataWebSupport
@EnableJpaRepositories
@EnableGlobalMethodSecurity(prePostEnabled = true)
@ComponentScan(basePackages = { "mypackage.spring.rest" }, excludeFilters = @Filter(type = FilterType.ANNOTATION, value = Configuration.class))
public class SpringRestConfiguration {

}

它有效!

【讨论】:

    【解决方案2】:

    TLDR:可以改用 WebConfig 上的@EnableAspectJAutoProxy(proxyTargetClass = true)

    问题的根源可能是 Spring 不生成控制器的代理类 - 默认情况下,Spring 包装到代理中,只有那些定义为接口的 bean 和 Spring IoC 找到它们的实现。如果您的控制器是不实现/扩展任何东西的类,则必须使用 CGLIB 代理(我建议阅读有关 Spring Proxying mechanisms 的内容),因此会生成代理类并将其作为控制器实现注入 - 这就是 Spring 包含额外逻辑的地方尊重@PreAuthorize@PostAuthorize 注释条件。

    在 Spring v5(不是 Spring Boot)上,当 @EnableGlobalMethodSecurity(prePostEnabled = true) 仅在 SecurityConfiguration 上使用时,WebConfig 不会拾取它。将其移动到 WebConfig 将启用 Spring Security 处理 pre post annotations 以及它将打开 CGLIB 代理机制。

    就个人而言,我建议在 WebConfig 中添加 @EnableAspectJAutoProxy(proxyTargetClass = true) 并将 @EnableGlobalMethodSecurity 留在 SecurityConfig 中。

    我仅在 Spring v5 上对其进行了测试,但由于文档的原因,它在 Spring v4 上应该可以正常工作。

    【讨论】:

      猜你喜欢
      • 2017-01-10
      • 2012-08-04
      • 2019-04-27
      • 2012-08-22
      • 2011-07-03
      • 1970-01-01
      • 2016-11-16
      • 2015-01-30
      • 2014-06-14
      相关资源
      最近更新 更多