source

스프링 보안에서는 액세스가 항상 거부됩니다(Deny All Permission Evaluator).

gigabyte 2023. 2. 14. 21:27
반응형

스프링 보안에서는 액세스가 항상 거부됩니다(Deny All Permission Evaluator).

Spring Boot 어플리케이션에서 ACL을 설정했습니다.ACL 의 설정은 다음과 같습니다.

@Configuration
@ComponentScan(basePackages = "com.company")
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class ACLConfigration extends GlobalMethodSecurityConfiguration {

    @Autowired
    DataSource dataSource;

    @Bean
    public EhCacheBasedAclCache aclCache() {
        return new EhCacheBasedAclCache(aclEhCacheFactoryBean().getObject(), permissionGrantingStrategy(), aclAuthorizationStrategy());
    }

    @Bean
    public EhCacheFactoryBean aclEhCacheFactoryBean() {
        EhCacheFactoryBean ehCacheFactoryBean = new EhCacheFactoryBean();
        ehCacheFactoryBean.setCacheManager(aclCacheManager().getObject());
        ehCacheFactoryBean.setCacheName("aclCache");
        return ehCacheFactoryBean;
    }

    @Bean
    public EhCacheManagerFactoryBean aclCacheManager() {
        return new EhCacheManagerFactoryBean();
    }

    @Bean
    public DefaultPermissionGrantingStrategy permissionGrantingStrategy() {
        ConsoleAuditLogger consoleAuditLogger = new ConsoleAuditLogger();
        return new DefaultPermissionGrantingStrategy(consoleAuditLogger);
    }

    @Bean
    public AclAuthorizationStrategy aclAuthorizationStrategy() {
        return new AclAuthorizationStrategyImpl(new SimpleGrantedAuthority("ROLE_ACL_ADMIN"));
    }

    @Bean
    public LookupStrategy lookupStrategy() {
        return new BasicLookupStrategy(dataSource, aclCache(), aclAuthorizationStrategy(), new ConsoleAuditLogger());
    }

    @Bean
    public JdbcMutableAclService aclService() {
        return new JdbcMutableAclService(dataSource, lookupStrategy(), aclCache());
    }

    @Bean
    public DefaultMethodSecurityExpressionHandler defaultMethodSecurityExpressionHandler() {
        return new DefaultMethodSecurityExpressionHandler();
    }

    @Override
    public MethodSecurityExpressionHandler createExpressionHandler() {
        DefaultMethodSecurityExpressionHandler expressionHandler = defaultMethodSecurityExpressionHandler();
        expressionHandler.setPermissionEvaluator(new AclPermissionEvaluator(aclService()));
        expressionHandler.setPermissionCacheOptimizer(new AclPermissionCacheOptimizer(aclService()));
        return expressionHandler;
    }
}

참고 자료:

보안 설정은 다음과 같습니다.

@Configuration
@EnableWebSecurity
public class CustomSecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Bean
    public AuthenticationEntryPoint entryPoint() {
        return new LoginUrlAuthenticationEntryPoint("/authenticate");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http
                .csrf()
                .disable()
                .authorizeRequests()
                .antMatchers("/authenticate/**").permitAll()
                .anyRequest().fullyAuthenticated()
                .and().requestCache().requestCache(new NullRequestCache())
                .and().addFilterBefore(authenticationFilter(), CustomUsernamePasswordAuthenticationFilter.class);
    }

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

    @Bean
    public CustomUsernamePasswordAuthenticationFilter authenticationFilter()
            throws Exception {
        CustomUsernamePasswordAuthenticationFilter authenticationFilter = new CustomUsernamePasswordAuthenticationFilter();
        authenticationFilter.setUsernameParameter("username");
        authenticationFilter.setPasswordParameter("password");
        authenticationFilter.setFilterProcessesUrl("/authenticate");
        authenticationFilter.setAuthenticationSuccessHandler(new CustomAuthenticationSuccessHandler());
        authenticationFilter.setAuthenticationFailureHandler(new CustomAuthenticationFailureHandler());
        authenticationFilter.setAuthenticationManager(authenticationManagerBean());
        return authenticationFilter;
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

★★★CustomAuthenticationProvider 링크:

@Component
public class CustomAuthenticationProvider implements AuthenticationProvider {

    @Autowired
    private UsersService usersService;

    @Override
    public Authentication authenticate(Authentication authentication)
            throws AuthenticationException {

        String username = authentication.getName();
        String password = authentication.getCredentials().toString();

        User user = usersService.findOne(username);

        if(user != null && usersService.comparePassword(user, password)){

            return new UsernamePasswordAuthenticationToken(
                    user.getUsername(),
                    user.getPassword(),
                    AuthorityUtils.commaSeparatedStringToAuthorityList(
                            user.getUserRoles().stream().collect(Collectors.joining(","))));
        } else {
            return null;
        }
    }

    @Override
    public boolean supports(Class<?> authentication) {
        return authentication.equals(UsernamePasswordAuthenticationToken.class);
    }
}

여기 .CustomUsernamePasswordAuthenticationToken:

public class CustomUsernamePasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter {

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
            throws AuthenticationException {

        if(!request.getMethod().equals("POST"))
            throw new AuthenticationServiceException(String.format("Authentication method not supported: %s", request.getMethod()));

        try {

            CustomUsernamePasswordAuthenticationForm form = new ObjectMapper().readValue(request.getReader(), CustomUsernamePasswordAuthenticationForm.class);

            String username = form.getUsername();
            String password = form.getPassword();

            if(username == null)
                username = "";

            if(password == null)
                password = "";

            UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, password);

            setDetails(request, token);

            return getAuthenticationManager().authenticate(token);

        } catch (IOException exception) {
            throw new CustomAuthenticationException(exception);
        }
    }

    private class CustomAuthenticationException extends RuntimeException {
        private CustomAuthenticationException(Throwable throwable) {
            super(throwable);
        }
    }
}

에도 기기, , 있습니다.CustomAuthenticationFailureHandler,CustomAuthenticationSuccessHandler,CustomNoRedirectStrategy ★★★★★★★★★★★★★★★★★」CustomUsernamePasswordAuthenticationForm츠키노

그리고 여기에 있는 MySQL 스키마를 사용하고 있습니다.

다음과 같이 acl 관련 테이블에 엔트리를 추가합니다.

INSERT INTO acl_class VALUES (1, com.company.project.domain.users.User)
INSERT INTO acl_sid VALUES (1, 1, "demo")

((사용자명)을 demo)

INSERT INTO acl_object_identity VALUES (1, 1, 1, NULL, 1, 0)
INSERT INTO acl_entry VALUES (1, 1, 1, 1, 1, 1, 1, 1)

하지만 내가 얻는 건

Denying user demo permission 'READ' on object com.company.project.domain.users.User@4a49e9b4

내 안에서

@PostFilter("hasPermission(filterObject, 'READ')")

여기서 몇 가지 문제가 의심됩니다.

  1. hasPermission 표시:'읽다'는 '1', '1', '읽다'를 말합니다.
  2. 데이터베이스 입력이 올바르지 않습니다.
  3. 커스텀 허가 평가자를 실장하고 있지 않습니다. "" " " " " " 입니다.expressionHandler.setPermissionEvaluator(new AclPermissionEvaluator(aclService()));★★★★★★★★★★★★★★★★★★?

갱신하다

「」의 샘플 :@PostFilter사용되는 것은 다음과 같습니다.

@RequestMapping(method = RequestMethod.GET)
    @PostFilter("hasPermission(filterObject, 'READ')")
    List<User> find(@Min(0) @RequestParam(value = "limit", required = false, defaultValue = "10") Integer limit,
                    @Min(0) @RequestParam(value = "page", required = false, defaultValue = "0") Integer page,
                    @RequestParam(value = "email", required = false) String email,
                    @RequestParam(value = "firstName", required = false) String firstName,
                    @RequestParam(value = "lastName", required = false) String lastName,
                    @RequestParam(value = "userRole", required = false) String userRole) {

        return usersService.find(
                limit,
                page,
                email,
                firstName,
                lastName,
                userRole);
    }

업데이트 #2:

이 질문에는 인증/허가/ACL과 관련하여 설정된 모든 내용이 반영됩니다.

업데이트 3:

이제 문제 해결에 매우 가까워졌습니다.이제 이 문제만 해결하면 됩니다.

https://stackoverflow.com/questions/42996579/custom-permissionevaluator-not-called-although-set-as-permissionevaluator-deny

만약 누군가가 그 질문에 대해 나를 도와줄 수 있다면, 나는 마침내 이 문제를 해결하기 위해 내가 겪은 일을 쓸 수 있을 것이다.

스프링 시큐리티 4.2.1. 후, 되어, 예기치 않은 했습니다.@PreAuthorize주석이 달린 메서드는 업그레이드 직전에 정상적으로 동작했습니다.스프링 보안 코드를 디버깅한 결과, 아래 코드와 같이 기본 프레픽스를 empty로 설정했는데도 불구하고 체크되는 모든 역할에 기본 문자열 "ROLE_"이 프리픽스 되어 있는 것이 문제임을 알게 되었습니다.

auth.ldapAuthentication()
        .groupSearchBase(ldapProperties.getProperty("groupSearchBase"))
        .groupRoleAttribute(ldapProperties.getProperty("groupRoleAttribute"))
        .groupSearchFilter(ldapProperties.getProperty("groupSearchFilter"))

        //this call used to be plenty to override the default prefix
        .rolePrefix("")

        .userSearchBase(ldapProperties.getProperty("userSearchBase"))
        .userSearchFilter(ldapProperties.getProperty("userSearchFilter"))
        .contextSource(this.ldapContextSource);

모든 컨트롤러 메서드에는 다음과 같은 주석이 붙어 있습니다.@PreAuthorize("hasRole('my_ldap_group_name')")그러나 프레임워크는 빈 역할 프리픽스 설정을 고려하지 않았기 때문에 ROLE_my_ldap_group_name을 사용하여 실제 역할을 체크하고 있었습니다.

제가 프레임워크의 코드를 깊이 파고든 후, 저는 그 수업이org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler가 「Default Prefix」로 ."ROLE_", 우선 「Declared bean」의 「을 것을 알 수 org.springframework.security.config.core.GrantedAuthorityDefaults 빈 시org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer 이 bean」을 하고

동작이 .Spring Security ldap role Prefix spring spring spring 。이 하기 위해 콩을 .org.springframework.security.config.core.GrantedAuthorityDefaults응용 프로그램 컨텍스트(주석 기반 구성을 사용 중)에 다음과 같이 표시됩니다.

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

    private static final String ROLE_PREFIX = "";
    //... ommited code ...
    @Bean
    public GrantedAuthorityDefaults grantedAuthorityDefaults() {
        return new GrantedAuthorityDefaults(ROLE_PREFIX);
    }

}

같은 문제가 발생할 수 있습니다.DefaultMethodSecurity를 사용하고 있는 것을 알 수 있습니다.Expression Handler 및 Gented 빈을 사용합니다.AuthorityDefaults, 즉 나와 동일한 Spring Security 버전 4.2.1을 사용하는 경우.릴리스에서는, 같은 문제가 발생하고 있을 가능성이 있습니다.

오랫동안 기다려온 답변은 다음과 같습니다.

문서에는 다음 사항이 명확하게 설명되어 있습니다.

hasPermission() 식을 사용하려면 응용 프로그램 컨텍스트에서 PermissionEvaluator를 명시적으로 설정해야 합니다.이것은 다음과 같습니다.

으로 나는 내 래 my my my my my my my my my my my my my 에서 하고 있었다.AclConfiguration되어 있다GlobalMethodSecurityConfiguration:

    @Override
    protected MethodSecurityExpressionHandler createExpressionHandler() {
        DefaultMethodSecurityExpressionHandler expressionHandler = new DefaultMethodSecurityExpressionHandler();
        expressionHandler.setPermissionEvaluator(new AclPermissionEvaluator(aclService()));
        expressionHandler.setPermissionCacheOptimizer(new AclPermissionCacheOptimizer(aclService()));
        return expressionHandler;
    }

그건 봄까지 처리되지 않았어!

했다.AclConfig ★★★★★★★★★★★★★★★★★」GlobalMethodSecurityConfiguration ★★★★★★★★★★★★★가 있는 경우@Bean후자에 정의되어 있는 경우, 위의 방법은 처리되지 않습니다.이것은 버그일 가능성이 있습니다(그렇지 않은 경우는, 주제에 관한 어떠한 설명도 환영합니다).

DB를 사용합니다. 용 i i i i를 쓴다.@PostFilter("hasPermission(filterObject, 'READ')")★★★★★★★★★★★★★★★★★★.

User Details를 확장한 사용자 클래스가 db에 있는 get Username()을 통해 동일한 사용자 이름을 반환하고 있는지 확인합니다.보안과 앱이 동일한 컨텍스트에 있는지 확인합니다.

hasPermission 메서드는 인증 개체를 첫 번째 매개 변수로 받아들입니다.

boolean hasPermission(Authentication authentication,
                      Object targetDomainObject,
                      Object permission)

Authentication 객체는 구현 클래스이며 보통 Username Password입니다.인증토큰. 따라서 getPrincipal() 메서드는 getUserName() 메서드를 가진 개체를 반환해야 합니다. 메서드는 DB에 있는 것과 동일한 개체를 반환해야 합니다.

PrincipalSid 보기

public PrincipalSid(Authentication authentication) {
    Assert.notNull(authentication, "Authentication required");
    Assert.notNull(authentication.getPrincipal(), "Principal required");

    if (authentication.getPrincipal() instanceof UserDetails) {
        this.principal = ((UserDetails) authentication.getPrincipal()).getUsername();
    }
    else {
        this.principal = authentication.getPrincipal().toString();
    }
}

ㅇㅇㅇ. ㅇㅇㅇㅇㅇㅇ.AclPermissionEvaluator존중받지 못합니다.이동 중에 같은 문제가 발생하여 두 단계로 해결되었습니다.

  1. 주입하지 않고 재정의된 메서드 내에 식 핸들러를 만들었습니다.
  2. WebSecurityConfig와 같은 클래스에서 작성되었습니다.GlobalMethodSecurityConfiguration그 이외의 경우,GlobalMethodSecurityConfiguration는 동작하지 않으며 핸들러를 덮어쓰지도 않습니다.

언급URL : https://stackoverflow.com/questions/41872192/access-is-always-denied-in-spring-security-denyallpermissionevaluator

반응형