Java 在spring security中尝试访问登录页面时出现访问被拒绝异常

Java 在spring security中尝试访问登录页面时出现访问被拒绝异常,java,spring,spring-security,access-denied,spring-java-config,Java,Spring,Spring Security,Access Denied,Spring Java Config,我正在使用基于java的spring安全性。我已经创建了自定义访问决策投票者impl 但是当我运行应用程序时,我无法打开登录页面,因为它说,访问被拒绝 这是在我添加自定义访问决策投票者impl之后发生的。我猜问题是因为CustomAccessDecisionVoter中的以下代码 if(authentication instanceof AnonymousAuthenticationToken) return ACCESS_DENIED; 但我需要这样做,以便不检查未登

我正在使用基于java的spring安全性。我已经创建了自定义访问决策投票者impl

但是当我运行应用程序时,我无法打开登录页面,因为它说,访问被拒绝

这是在我添加自定义访问决策投票者impl之后发生的。我猜问题是因为CustomAccessDecisionVoter中的以下代码

if(authentication instanceof AnonymousAuthenticationToken)
            return ACCESS_DENIED;
但我需要这样做,以便不检查未登录用户的权限

它在无限循环中运行,登录页面、访问决策投票者、访问拒绝、登录页面等等

下面是spring安全配置代码

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private AffirmativeBased accessDecisionManager;

    @Bean
    @Autowired
    public AffirmativeBased accessDecisionManager(AccessDecisionVoterImpl accessDecisionVoter) {
        List<AccessDecisionVoter<?>> accessDecisionVoters = new ArrayList<AccessDecisionVoter<?>>();
        accessDecisionVoters.add(accessDecisionVoter);
        AffirmativeBased accessDecisionManager = new AffirmativeBased(accessDecisionVoters);
        return accessDecisionManager;
    }

    @Override
    @Autowired
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
    }

    @Bean
    public PasswordEncoder passwordEncoder(){
        PasswordEncoder passwordEncoder = new PasswordEncoder();
        passwordEncoder.setStringDigester(stringDigester());
        return passwordEncoder;
    }

    @Bean
    public PooledStringDigester stringDigester() {
        PooledStringDigester psd = new PooledStringDigester();

        psd.setPoolSize(2);
        psd.setAlgorithm("SHA-256");
        psd.setIterations(1000);
        psd.setSaltSizeBytes(16);
        psd.setSaltGenerator(randomSaltGenerator());

        return psd;
    }

    @Bean
    public RandomSaltGenerator randomSaltGenerator() {
        RandomSaltGenerator randomSaltGenerator = new RandomSaltGenerator();
        return randomSaltGenerator;
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring()
                .antMatchers("/static/**")
                .antMatchers("/i18n/**");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .csrf()
        .and()
            .formLogin()
            .loginPage("/login")
            .loginProcessingUrl("/checkLogin")
            .defaultSuccessUrl("/home")
            .failureUrl("/login?login_error=1")
            .usernameParameter("username")
            .passwordParameter("password")
            .permitAll()
        .and()
            .logout()
            .logoutUrl("/logout")
            .logoutSuccessUrl("/login?isLoggedOut=1")
            .deleteCookies("JSESSIONID")
            .invalidateHttpSession(true)
            .permitAll()
        .and()
            .authorizeRequests()
            .antMatchers("/login**").permitAll()
            .antMatchers("/error**").permitAll()
            .antMatchers("/checkLogin**").permitAll()
            .anyRequest()
            .authenticated()
            .accessDecisionManager(accessDecisionManager)
        .and()
            .exceptionHandling()
            .accessDeniedPage("/accessDenied")
        .and()
            .headers()
            .frameOptions()
            .disable()
        .and()
            .sessionManagement()
            .invalidSessionUrl("/login")
            .maximumSessions(1);
    }

}
此外,当我从我的投票者中删除以下行时,如果用户未登录并尝试访问受保护的页面,它将继续。它应该重定向到登录页面

if(authentication instanceof AnonymousAuthenticationToken)
                return ACCESS_DENIED;
这是我第一次尝试使用弹簧靴。因此,我不确定所有的配置问题

蚂蚁配对的顺序有什么问题吗


请提供帮助。

如果我没有看错,您正试图通过返回拒绝匿名身份验证的访问来解决用户匿名登录的问题。那不好。删除该自定义代码,改为在安全配置中执行此操作:

anyRequest().hasRole("USER")
然后,确保用户登录时具有所需的角色

另一种方法是禁用匿名登录,这样您就不需要角色(从长远来看,不建议这样做)


问题来自你的选民。 如果相关的“ConfigAttribute”配置为匿名访问,则投票者应允许匿名访问。 下面是您应该添加的内容(可能取决于您的Spring Security版本):

你的需要

  • 弃权投票(
    AccessDecisionVoter.ACCESS\u弃权)
    ):如果投票人无法做出决定(例如用户未经授权,无法从请求上下文获取模块等)
  • 授予访问权限(
    AccessDecisionVoter.access\u grated
    ):如果可以识别模块并且用户已被授权
  • 拒绝访问(
    AccessDecisionVoter.access\u DENIED
    ):如果可以识别模块且用户未经授权
使用
AccessDecisionManager
配置,基本上可以取消基于url的访问限制,例如:

http.authorizeRequests()
    .antMatchers("/css/**", "/img/**", "/js/**", "/font/**").permitAll()
    .antMatchers("/login**").permitAll()
    .antMatchers("/error**").permitAll()
    .antMatchers("/checkLogin**").permitAll()
    .anyRequest()
        .authenticated()
默认情况下,spring使用。 但是,如果至少有一个AccessDecisionVoter授予对资源的访问权,则会授予访问权(这可能不是您想要的)。 根据您的需求,AccessDecisionManager(包括
WebExpressionVoter
将是最佳匹配

@Bean
public AccessDecisionManager accessDecisionManager() {
    List<AccessDecisionVoter<? extends Object>> decisionVoters = new ArrayList<>();
    decisionVoters.add(new WebExpressionVoter());
    decisionVoters.add(new ModuleAccessDecisionVoter());
    ConsensusBased consensusBased = new ConsensusBased(decisionVoters);

    // deny access if voters equally voted to allow and deny access
    consensusBased.setAllowIfEqualGrantedDeniedDecisions(false);
    return consensusBased;
}

匿名访问应导致以下结果:

  • /login
    :WebExpressionVoter:
    +1
    ,模块选择器:
    0
    ->
    1
    =
    访问权
  • /foo module
    :WebExpressionVoter:
    -1
    ,ModuleVoter:
    -1
    -2=
    访问被拒绝
给定允许查看
Foo
模块的用户,应产生以下结果:

  • /foo module
    :WebExpressionVoter:
    +1
    ,ModuleVoter:
    +1
    ->
    2
    =
    访问权
  • /bar module
    :WebExpressionVoter:
    +1
    (因为用户已通过身份验证),ModuleVoter:
    -1
    ->
    0
    =
    访问被拒绝
    (因为
    基于共识。setAllowiequalrantedDeniedDecitions(false)

为什么要添加
AccessDecisionVoterImpl
?我想检查用户是否具有访问安全内容所需的权限。当用户登录时,我正在会话中保存他的权限,如addUser、activateUser等。当他访问一个url时,我在url中传递这样的权限,然后在会话中检查权限。这就是我在AccessDecisionVoterImpl中所做的,不要为此使用
AccessDecisionVoter
。您可以在
antMatcher
中使用
hasRole
来确保访问权限。您可以替换
///一些其他代码来检查基于角色模块的权限使用真实代码或至少部分代码更新了代码。基本上,模块将类似于用户,其上的操作将类似于addUser、deleteUser、listUser、editUser、activateUser等。基本上,我要做的是在应用程序中使用基于模块操作的授权。任何具有有效登录名的人都可以登录到应用程序。登录后,只有那些有权访问特定操作的用户才能访问该操作。匿名用户或未登录的用户仍应访问登录页面。但在我的例子中,即使是登录页面也没有显示为未登录用户,因为它说访问被拒绝。是的,我的回答(编辑)有点过火了。但如果我理解正确的话,问题是由于自定义身份验证投票者,您甚至无法访问将检查permitAll()的部分,因此不允许匿名访问应用程序的任何部分。通过确保至少有一个用户角色(通常是角色\用户),可以更容易地区分非任意登录。是的,如何绕过这一点?但投票者不应该在身份验证之后而不是之前被调用吗?投票者会被调用所有安全资源。因此,即使在登录页面上也会检查它,用户通常会在匿名登录时访问该页面。
for (ConfigAttribute attribute : collection) {
    if ("IS_AUTHENTICATED_ANONYMOUSLY".equals(attribute.getAttribute())) {
        return ACCESS_GRANTED;
    }
}
http.authorizeRequests()
    .antMatchers("/css/**", "/img/**", "/js/**", "/font/**").permitAll()
    .antMatchers("/login**").permitAll()
    .antMatchers("/error**").permitAll()
    .antMatchers("/checkLogin**").permitAll()
    .anyRequest()
        .authenticated()
@Bean
public AccessDecisionManager accessDecisionManager() {
    List<AccessDecisionVoter<? extends Object>> decisionVoters = new ArrayList<>();
    decisionVoters.add(new WebExpressionVoter());
    decisionVoters.add(new ModuleAccessDecisionVoter());
    ConsensusBased consensusBased = new ConsensusBased(decisionVoters);

    // deny access if voters equally voted to allow and deny access
    consensusBased.setAllowIfEqualGrantedDeniedDecisions(false);
    return consensusBased;
}
static class ModuleAccessDecisionVoter implements AccessDecisionVoter<FilterInvocation> {

    public int vote(Authentication authentication, FilterInvocation object, Collection<ConfigAttribute> attributes) {
        if (authentication == null || authentication instanceof AnonymousAuthenticationToken) {
            return ACCESS_ABSTAIN;
        }

        // determine module and grant or deny access
        // if module cannot be determined abstain from voting
        String module = determineModule(object);
        if (module != null) {
            return isAccessGranted(module, authentication) ? ACCESS_GRANTED : ACCESS_DENIED
        }

        return ACCESS_ABSTAIN;
    }
}