如何从Spring MVC \ Boot Controller方法正确检索记录的用户信息?

时间:2017-01-23 13:35:56

标签: java spring spring-mvc spring-boot spring-security

我是 Spring Security 的新手,我遇到了以下问题。

我正在使用 Spring Security 处理 Spring Boot 项目,以保护所有资源为 / Extranet / &#34;。< / p>

基本上我的 WebSecurityConfig 配置类包含以下代码:

@Configuration
@EnableWebSecurity
@ComponentScan(basePackageClasses = CustomUserDetailsService.class)
@EnableAutoConfiguration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired 
    private UserDetailsService userDetailsService;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
            .antMatchers("/Extranet/**").access("hasRole('ROLE_USER')")
            .anyRequest().permitAll()
            .and()
            .httpBasic()
            .and()
            .csrf().disable();
    }
}

工作正常,因为 / Extranet / login 可以访问资源我必须设置基本身份验证并指定正确的用户名和密码(执行请求)使用 Postman 工具进行测试。)

好的,这很好用。

在我的Spring Security配置中,调用了 CustomUserDetails 类,该类实现了Spring Security界面 UserDetails

public class CustomUserDetails extends User implements UserDetails {

    private static final long serialVersionUID = 1L;


    public CustomUserDetails(User user){
        super(user);
    }


    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        Set<GrantedAuthority> authorities = new HashSet<GrantedAuthority>();
        for(UserRole role : this.getUserRoles() ){
            GrantedAuthority grantedAuthority = new SimpleGrantedAuthority(role.getName());
            authorities.add(grantedAuthority);
        }

        return authorities;
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }
    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }
    @Override
    public boolean isEnabled() {
        return true;
    }

    @Override
    public String getUsername() {
        return super.getUsername();
    }

}

此对象的实例包含当前记录的用户的用户详细信息。

我怀疑的是:如何从控制器方法中检索此对象? (我认为这应该是在上下文中,我可以以某种方式检索它。)

我试图这样做:

@RestController
@RequestMapping("/Extranet")
public class AccessController {

    @RequestMapping(value = "/login", method = RequestMethod.POST)
    public ResponseEntity<String> login(CustomUserDetails userInfo) {

        System.out.println("login() START");

        return ResponseEntity.ok("LOGGED IN");
    }
}

但是通过这种方式我获得了这样的例外:

[ERROR] 2017-01-23 14:18:04 [com.betrivius.controller.exceptionHandler.ControllerExceptionHandler.handleException(ControllerExceptionHandler.java:106)] [http-nio-8080-exec-1] ControllerExceptionHandler - Request: http://localhost:8080/Extranet/login  throws: 
org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.betrivius.security.bean.CustomUserDetails]: No default constructor found; nested exception is java.lang.NoSuchMethodException: com.betrivius.security.bean.CustomUserDetails.<init>()
    at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:105) ~[spring-beans-4.3.3.RELEASE.jar:4.3.3.RELEASE]

它似乎无法实例化 CustomUserDetails 对象。为什么呢?

据我所知,我可以检索与已登录用户相关的 CustomUserDetails 对象。我该怎么办?

我也试着这样做:

@RequestMapping(value = "/login", method = RequestMethod.POST)
public ResponseEntity<String> login(Principal principal) {

    System.out.println("login() START");

    return ResponseEntity.ok("LOGGED IN");
}

通过这种方式, principal 参数被正确实例化,并包含前一个 CustomUserDetails 对象的实例,其中包含已记录用户的信息。

这是访问当前登录用户信息的正确方法吗?

另一个疑问是:为什么我可以将 Principal principal 参数传递给我的 login()方法?引擎盖下究竟发生了什么?谁有效地将其传递给 login()方法?

我认为应该发生这样的事情:

1)对于&#34; / Extranet / login&#34;有一个POST HttpRequest。资源。调度程序servlet将其发送到 login()方法。

2) Principal principal 被放入Spring Context后,用户启用了此资源(之前调用了控制器方法),因此Spring工厂可以从上下文中检索它将其传递给 login()方法。

但我绝对不确定。究竟如何运作?我错过了什么?

1 个答案:

答案 0 :(得分:2)

您可能需要@AuthenticationPrincipal注释:

@RequestMapping(value = "/login", method = RequestMethod.POST)
public ResponseEntity<String> login(@AuthenticationPrincipal CustomUserDetails userInfo) {

    System.out.println("login() START");

    return ResponseEntity.ok("LOGGED IN");
}

如果仍然无法解决问题,请尝试使用旧方法进行调试:

@RequestMapping(value = "/login", method = RequestMethod.POST)
public ResponseEntity<String> login() {

    CustomUserDetails userInfo = (CustomerUserDetails)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
    ...
}