복붙노트

[SPRING] EnableWebSecurity를 ​​사용할 때 AuthenticationPrincipal이 비어 있습니다.

SPRING

EnableWebSecurity를 ​​사용할 때 AuthenticationPrincipal이 비어 있습니다.

Spring Security doc : 34.1 @EnableWebMvcSecurity에서, @EnableWebMvcSecurity는 @EnableWebSecurity로 바뀌었다.

그러나 @AuthenticationPrincipal에 의해 컨트롤러에서 UserDetails를 가져 오려고하면 빈 개체가 생깁니다. 사용자 이름은 ""입니다. 또한 @EnableWebMvcSecurity를 ​​시도했지만 불행히도 UserDetails는 null입니다.

그러나 나는 다음과 같이 전통적인 방식으로 UserDetails를 얻을 수있다.

SecurityContextHolder.getContext().getAuthentication().getPrincipal();

제 질문은 @EnableWebSecurity를 ​​사용할 때 내 맞춤 UserDetails (계정)를 얻는 올바른 방법은 무엇입니까?

다음은 관련 소스 코드입니다.

제어 장치:

@RequestMapping(method = RequestMethod.POST)
@Secured("ROLE_USER")
public String postRoom(@Valid @ModelAttribute Room room, BindingResult result, Model model, @AuthenticationPrincipal Account principal) {
    if (result.hasErrors()) {
        return "room_form";
    }

    Account account = accountRepository.findByUsername(principal.getUsername());
    room.setAccountId(account.getId());
    room.setLastModified(new Date());
    roomRepository.save(room);
    return "room_list";
}

보안 구성 :

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private DataSource dataSource;

    @Autowired
    private SecurityProperties security;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests().anyRequest().permitAll()
            .and().formLogin().loginPage("/login").failureUrl("/login?error").permitAll()
            .and().logout().permitAll()
            .and().rememberMe()
            .and().csrf().disable();
    }

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
            auth.jdbcAuthentication().dataSource(this.dataSource).passwordEncoder(new BCryptPasswordEncoder(8));
    }
}

그리고 Account.java :

@Entity
@Table(name = "users")
public class Account implements Serializable {
    @Id
    @GeneratedValue
    private Long id;

    private String username;
    private String password;
    private boolean enabled;

    @Lob
    private byte[] avatar;

// getter / setter ...
}

해결법

  1. ==============================

    1.이 게시물에 대한 의견 중 하나에서 언급했듯이, 반환하는 Account 클래스는 authentication.getPrincipal ()에서 할당 할 수 있어야합니다. 즉, Account 클래스가 org보다 UserDetails 인터페이스를 구현해야 할 가능성이 높습니다. springframework.security.core.userdetails.User 클래스가 수행합니다. UserDetails 또는이 페이지에 대한 Java의 API 문서를 살펴보십시오.

    이 게시물에 대한 의견 중 하나에서 언급했듯이, 반환하는 Account 클래스는 authentication.getPrincipal ()에서 할당 할 수 있어야합니다. 즉, Account 클래스가 org보다 UserDetails 인터페이스를 구현해야 할 가능성이 높습니다. springframework.security.core.userdetails.User 클래스가 수행합니다. UserDetails 또는이 페이지에 대한 Java의 API 문서를 살펴보십시오.

    @AuthenticationPrincipal 이후의 유형을 User로 변경하면 null 문제가 사라질 수 있습니다.

    데이터베이스 설정 방법에 따라 Account 클래스가 UserDetails를 구현하게되면 모델로 간주되기 때문에 Account에 너무 많은 로직이 도입 될 수 있습니다.

    @EnableWebSecurity를 ​​사용하기 때문에 일부 게시물에서 제안하는 것처럼 사용자 정의 HandlerMethodArgumentResolver를 구성 할 필요가 없습니다.

    참고 스프링 부트 사용

    jdbcAuthentication을 사용하려면 사전 정의 된 방식으로 데이터베이스를 설정해야합니다 (그리고 가장 많이 염두에 둔 스키마는 작성한 스키마와 같지 않습니다). 가장 좋은 방법은 사용자 정의 스키마를 만들고 사용자 인증 서비스를 구성하는 것입니다 (어렵지 않다). 개인적으로 configureGlobal ... 메소드에서 사용자 정의 사용자 인증 서비스를 구성했습니다. 짧막 한 농담.

    auth.userDetailsService(userService).passwordEncoder...
    

    내 사용자 정의 userService는 공용 UserDetails를 구현할 수있는 하나의 메소드 만 제공하는 UserDetailsService 인터페이스를 구현합니다. loadUserByUsername (String username) Spring Security는 메소드를 호출하여 사용자를 인증합니다. 그 loadUserByUsername 메소드 내에서 데이터베이스로부터 사용자 정보를 검색하고 새로운 org.springframework.security.core.userdetails.User 객체를 만들고 사용자 이름, 비밀번호 및 사용 권한을 User 객체에 채운 다음 반환했습니다. 나는 나의 방법 끝에 다음과 같은 것을 가지고있다.

    return new User(user.getUsername(), user.getPassword(), permissions);
    

    UserDetails 인터페이스를 구현 한 User를 반환했기 때문에 @AuthenticationPrincipal User 사용자가 나를 대신 사용할 수 있습니다.

    사용 권한 변수는 GrantedAuthority 인터페이스 또는 해당 유형의 컬렉션을 구현하는 클래스 여야합니다. 이 인터페이스에는 public String getAuthority ()를 구현하는 단 하나의 메소드 만 있습니다. 기본적으로 원하는 문자열 (데이터베이스의 권한 / 역할 이름)을 반환 할 수 있습니다.

    Spring Security가 권한 부여를 다루는 방법을 알고 있다고 가정합니다. Spring Security가보다 세분화 된 제어를위한 그룹 + 권한 대신에 역할 기반 권한 부여 만 사용한다는 것은 슬픈 일입니다. 그러나 데이터베이스에 그룹 테이블과 권한 테이블을 만들고 해당 클래스가 GrantedAuthority 인터페이스를 구현하면이 문제를 해결할 수 있습니다. 당신은 효과적으로 두 범주로 "역할"을 분리 할 것입니다.

    @AuthenticationPrincipal을 통해 자신의 클래스를 사용하고 싶거나 사용자 이름과 암호 이상을 원한다면 아마 Account 클래스에서 래퍼 클래스를 만들면 (Account에서 로직을 빼내기 위해) 래퍼 클래스가 UserDetails를 구현하도록 할 것입니다.

    마지막으로 저장소 계층 위에 서비스 계층을 추가하고 컨트롤러가 서비스 계층과 통신하도록하는 것이 좋습니다. 이 설정은 해커가 저장소 계층에 도달하기 전에 해커가 서비스 계층을 해킹해야하므로 보안 계층을 추가하고 CRUD에 대해서만 사용해야하므로 저장소 계층 외부로 논리를 가져옵니다.

  2. ==============================

    2.나는 그것을 시도하지 않았지만 이것은 당신을 위해 일해야합니다 :

    나는 그것을 시도하지 않았지만 이것은 당신을 위해 일해야합니다 :

    @Configuration
    public class MvcConfig extends WebMvcConfigurerAdapter {
    
        @Bean
        public AuthenticationPrincipalArgumentResolver authenticationPrincipalArgumentResolver(){
            return new AuthenticationPrincipalArgumentResolver();
        }
    
        @Override
        public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
            argumentResolvers.add(authenticationPrincipalArgumentResolver());
        }
    }
    
  3. ==============================

    3.나에게 도움이되는 인수 결정자의 구성

    나에게 도움이되는 인수 결정자의 구성

    <mvc:annotation-driven>
        <mvc:argument-resolvers>
            <bean class="org.springframework.security.web.method.annotation.AuthenticationPrincipalArgumentResolver"/>
        </mvc:argument-resolvers>
    </mvc:annotation-driven>
    

    또한 34.2 @AuthenticationPrincipal 섹션을 볼 수 있습니다.

    이것이 내가 인수 분석자를 수동으로 추가해야한다는 것을 어떻게 이해하는지. 또는

    관련 질문

  4. ==============================

    4.나는 같은 문제를 만났다. 내 자신의 HandlerMethodArgumentResolver 및 주석을 만들어야했습니다. 다음 코드는 테스트를 거쳤으며 작동합니다.

    나는 같은 문제를 만났다. 내 자신의 HandlerMethodArgumentResolver 및 주석을 만들어야했습니다. 다음 코드는 테스트를 거쳤으며 작동합니다.

    먼저 주석을 작성하십시오.

    @Target({ ElementType.PARAMETER, ElementType.ANNOTATION_TYPE })
    @Retention(RetentionPolicy.RUNTIME)
    public @interface CurrentUser{}
    

    간단한 구성

    @Configuration
    @EnableWebMvc
    @ComponentScan(basePackages = "x.x.x")
    public class ApplicationConfiguration extends WebMvcConfigurerAdapter{
    
        @Override
        public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
            argumentResolvers.add(new HandlerMethodArgumentResolver() {
    
                public boolean supportsParameter(MethodParameter parameter) {
                    return findMethodAnnotation(CurrentUser.class, parameter) != null;
                }
    
                public Object resolveArgument(
                        MethodParameter parameter,
                        ModelAndViewContainer mavContainer,
                        NativeWebRequest webRequest,
                        WebDataBinderFactory binderFactory) throws Exception
                {
                    Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
                    Object principal = authentication.getPrincipal();
    
                    if(principal != null && !parameter.getParameterType().isAssignableFrom(principal.getClass()))
                        throw new ClassCastException(principal + " is not assignable to " + parameter.getParameterType());
    
                    return principal;
                }
    
                <T extends Annotation> T findMethodAnnotation(Class<T> annotationClass, MethodParameter parameter) {
                    T annotation = parameter.getParameterAnnotation(annotationClass);
                    if(annotation != null) {
                        return annotation;
                    }
                    Annotation[] annotationsToSearch = parameter.getParameterAnnotations();
                    for(Annotation toSearch : annotationsToSearch) {
                        annotation = AnnotationUtils.findAnnotation(toSearch.annotationType(), annotationClass);
                        if(annotation != null) {
                            return annotation;
                        }
                    }
                    return null;
                }
            });
        }
    }
    

    그런 다음 컨트롤러에서 사용하십시오.

    @RequestMapping("/userget")
    public User message(@CurrentUser User user){
        return user;
    }
    

    User는 UserDetails를 anyamore 확장 할 필요가 없습니다. 희망이 (의지) 도움이 (들)

  5. from https://stackoverflow.com/questions/29657039/authenticationprincipal-is-empty-when-using-enablewebsecurity by cc-by-sa and MIT license