简体   繁体   English

尝试访问spring security中的登录页面时访问被拒绝的异常

[英]Access denied exception while trying to access the login page in spring security

I am using java based spring security. 我正在使用基于java的spring security。 I have created the custom access decision voter impl. 我创建了自定义访问决策选民impl。

But when I run the application, I can not open the login page as it says, access is denied. 但是当我运行应用程序时,我无法打开登录页面,因为它说,访问被拒绝。

This happened after I added the custom access decision voter impl. 这是在我添加自定义访问决策选民impl之后发生的。 I guess the issue is because of the following code in custom AccessDecisionVoter. 我想问题是由于自定义AccessDecisionVoter中的以下代码。

if(authentication instanceof AnonymousAuthenticationToken)
            return ACCESS_DENIED;

But i need this so that permissions are not checked for not logged in users. 但我需要这样,以便不检查未登录用户的权限。

And it goes in infinite loop, login page, access decision voter, access denied, login page and so on. 它进入无限循环,登录页面,访问决策选民,访问被拒绝,登录页面等。

Below is the spring security configuration code. 下面是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);
    }

}

and my custom voter impl 和我的自定义选民impl

@Component
public class AccessDecisionVoterImpl implements AccessDecisionVoter {

    @Autowired
    private ModuleService moduleService;

    @Override
    public boolean supports(ConfigAttribute attribute) {
        return true;
    }

    @Override
    public boolean supports(Class clazz) {
        return true;
    }

    @Override
    public int vote(Authentication authentication, Object object, Collection collection) {
// i have given this so that if user is not logged in then should not check permission at all 
        if(authentication instanceof AnonymousAuthenticationToken)
            return ACCESS_DENIED;

             HttpServletRequest request = ((FilterInvocation) object).getHttpRequest();
    String requestedOperation = request.getParameter("operation");

    if (requestedOperation != null && !requestedOperation.isEmpty()){
        String [] requestURISplit = request.getRequestURI().split("/");
        String requestedModuleName = requestURISplit[2];

        if(SecurityUtils.hasPermission(requestedModuleName, requestedOperation)){
           return ACCESS_GRANTED;
        }
    } else {
        return ACCESS_GRANTED;
    }

    return ACCESS_DENIED; 

Also, when I remove following lines from my voter, if the user is not logged in and tries to access the secured pages, it goes ahead. 此外,当我从选民中删除以下行时,如果用户未登录并尝试访问受保护的页面,则会继续。 It should have redirected to the login page instead. 它应该已重定向到登录页面。

if(authentication instanceof AnonymousAuthenticationToken)
                return ACCESS_DENIED;

This is the first time I am trying to use the spring boot. 这是我第一次尝试使用弹簧靴。 Hence, i am not sure of all the configuration issues. 因此,我不确定所有的配置问题。

Is there anything wrong with the order of the antMatchers? antMatchers的顺序有什么问题吗?

Please help. 请帮忙。

Your AccessDecisionVoter needs to either 您的AccessDecisionVoter需要

  • abstain from voting ( AccessDecisionVoter.ACCESS_ABSTAIN ): if the voter is unable to make a decision (eg. user unauthorized, unable to obtain module from request context etc.) 弃权投票( AccessDecisionVoter.ACCESS_ABSTAIN ):如果选民无法做出决定(例如,用户未经授权,无法从请求上下文等获取模块)
  • grant access ( AccessDecisionVoter.ACCESS_GRANTED ): if the module can be identified and the user is authorized 授予访问权限( AccessDecisionVoter.ACCESS_GRANTED ):如果可以识别模块并且用户被授权
  • deny access ( AccessDecisionVoter.ACCESS_DENIED ): if the module can be identified and the user is not authorized 拒绝访问( AccessDecisionVoter.ACCESS_DENIED ):如果可以识别模块并且用户未被授权

With your AccessDecisionManager configuration, you basically cancel out the url-based access restrictions such as: 使用AccessDecisionManager配置,您基本上可以取消基于URL的访问限制,例如:

http.authorizeRequests()
    .antMatchers("/css/**", "/img/**", "/js/**", "/font/**").permitAll()
    .antMatchers("/login**").permitAll()
    .antMatchers("/error**").permitAll()
    .antMatchers("/checkLogin**").permitAll()
    .anyRequest()
        .authenticated()

By default spring uses the WebExpressionVoter for that purpose. 默认情况下,spring使用WebExpressionVoter来实现此目的。 However, the AffirmativeBased AccessDecisionManager grants access if at least one AccessDecisionVoter grants access to a resource (this is probably not what you want). 但是,如果至少有一个AccessDecisionVoter授予对资源的访问权限,则AffirmativeBased AccessDecisionManager将授予访问权限(这可能不是您想要的)。 For your requirements a ConsensusBased AccessDecisionManager including the WebExpressionVoter would be the best match. 根据您的要求,包含WebExpressionVoterConsensusBased AccessDecisionManager将是最佳匹配。

@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;
}

Your AccessDecisionVoter implementation: 您的AccessDecisionVoter实现:

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;
    }
}

Anonymous access should lead to the following results: 匿名访问应导致以下结果:

  • /login : WebExpressionVoter: +1 , ModuleVoter: 0 -> 1 = ACCESS_GRANTED /login :WebExpressionVoter: +1 ,ModuleVoter: 0 - > 1 = ACCESS_GRANTED
  • /foo-module : WebExpressionVoter: -1 , ModuleVoter: -1 -> -2 = ACCESS_DENIED /foo-module :WebExpressionVoter: -1 ,ModuleVoter: -1 - > -2 = ACCESS_DENIED

Given a User that is allowed to view the Foo module should produce the following results: 给定允许查看Foo模块的用户应该产生以下结果:

  • /foo-module : WebExpressionVoter: +1 , ModuleVoter: +1 -> 2 = ACCESS_GRANTED /foo-module :WebExpressionVoter: +1 ,ModuleVoter: +1 - > 2 = ACCESS_GRANTED
  • /bar-module : WebExpressionVoter: +1 (because user is authenticated), ModuleVoter: -1 -> 0 = ACCESS_DENIED (because of ConsensusBased.setAllowIfEqualGrantedDeniedDecisions(false) ) /bar-module :WebExpressionVoter: +1 (因为用户已通过身份验证),ModuleVoter: -1 - > 0 = ACCESS_DENIED (因为ConsensusBased.setAllowIfEqualGrantedDeniedDecisions(false)

If I read this correctly, you are trying to solve the problem where your user is being logged in anonymously by returning ACCESS_DENIED for anonymous authentication. 如果我正确读取此内容,您将尝试通过返回ACCESS_DENIED进行匿名身份验证来解决用户匿名登录的问题。 That's no good. 那不好。 Remove that custom code and instead do this in security config: 删除该自定义代码,而不是在安全配置中执行此操作:

anyRequest().hasRole("USER")

Then, ensure that when user is logged in, they have the required role. 然后,确保在用户登录时,他们具有所需的角色。

An alternative would be to disable anonymous login so you won't need roles (for long term this would not be recommended). 另一种方法是禁用匿名登录,这样您就不需要角色(长期不推荐这样做)。

http.anonymous().disable()

The problem comes from your voter. 问题来自你的选民。 Your voter should let anonymous access if the related "ConfigAttribute" is configured so. 如果配置了相关的“ConfigAttribute”,您的选民应该允许匿名访问。 Below is what you should add (it may depends on your Spring Security version): 以下是您应该添加的内容(可能取决于您的Spring Security版本):

for (ConfigAttribute attribute : collection) {
    if ("IS_AUTHENTICATED_ANONYMOUSLY".equals(attribute.getAttribute())) {
        return ACCESS_GRANTED;
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM