Spring Security @AuthenticationPrincipal

时间:2015-11-09 19:09:48

标签: java spring spring-security

我一直试图让@AuthenticationPrincipal与自定义User类一起正常工作。不幸的是,用户始终为空。这是代码:

控制器

@RequestMapping(value = "/", method = RequestMethod.GET)
public ModelAndView index(@AuthenticationPrincipal User user) {
    ModelAndView mav= new ModelAndView("/web/index");
    mav.addObject("user", user);
    return mav;
}

安全配置

@Configuration
@EnableWebMvcSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    CustomUserDetailsService customUserDetailsService;

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
            auth.userDetailsService(customUserDetailsService).passwordEncoder(passwordEncoder());
    }

}

CustomUserDetailsS​​ervice

@Component
public class CustomUserDetailsService implements UserDetailsService {

@Autowired
UserRepository userRepository;

@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    // Spring Data findByXY function
    return userRepository.findByUsername(username);
}

用户实体

public class User implements UserDetails{
    private String username;
    private String password;
    private Collection<Authority> authorities;

    // Getters and Setters

}

授权机构

public class Authority implements GrantedAuthority{
    private User user;
    private String role;

    // Getters and Setters

    @Override
    public String getAuthority() {
        return this.getRole();
    }
}

我尝试过在网上找到的各种解决方案,例如:转换我的自定义用户对象:

return new org.springframework.security.core.userdetails.User(user.getLogin(), user.getPassword(), true, true, true, true,  authorities);

获得活跃用户的其他方法正在顺利运行,但我发现@AuthenticationProvider CustomUserObject是最干净的方式,这就是为什么我想让它工作。非常感谢任何帮助。

9 个答案:

答案 0 :(得分:4)

您可以直接在方法参数中为经过身份验证的用户指定依赖项,而不是使用@AuthenticationPrincipal。下面给出的东西

@RequestMapping(value = "/", method = RequestMethod.GET)
public ModelAndView index(Principal user) {
    ModelAndView mav= new ModelAndView("/web/index");
    mav.addObject("user", user);
    return mav;
} 

此Principal对象将是通过spring security进行身份验证的实际对象。当调用该方法时,Spring将为您注入此内容。

答案 1 :(得分:3)

在我的情况下,我得到String返回(用户名)而不是UserDetails对象,即您应该将方法签名定义为

public ModelAndView index(@AuthenticationPrincipal String username)

这并不奇怪,因为@AuthenticationPrincipal实际上会返回Authentication.getPrincipal()并根据文档:

  

如果是使用用户名和密码的身份验证请求,   这将是用户名。呼叫者预计将填补   身份验证请求。

     

AuthenticationManager实现通常会返回一个   身份验证包含更丰富的信息作为使用的主体   通过申请。许多身份验证提供程序将创建一个   UserDetails对象作为主体。   请参阅:https://docs.spring.io/spring-security/site/docs/5.0.0.RELEASE/api/

所以,我假设你的AuthenticationManager实现只返回一个用户名

答案 2 :(得分:1)

@RequestMapping(method= RequestMethod.GET,value="/authenticate", produces = MediaType.APPLICATION_JSON_VALUE)
public Object authenticate(@AuthenticationPrincipal Object obj) {
    return obj;
}

我有同样的问题,我能够做到这一点。 您可以将它与mapper一起使用

@RequestMapping(method = RequestMethod.GET, value = "/authenticate2", produces = MediaType.APPLICATION_JSON_VALUE)
public User authenticate2(@AuthenticationPrincipal Object obj) throws IOException {
    return mapper.readValue(mapper.writeValueAsString(obj), User.class);
}

这些对我有用,我希望将来对任何人都有用

答案 3 :(得分:1)

摘自文档:

Class AuthenticationPrincipalArgumentResolver 将使用以下命令解析CustomUser参数 SecurityContextHolder中的Authentication.getPrincipal()。如果 Authentication或Authentication.getPrincipal()为null,它将 返回null。如果类型不匹配,则返回null,除非 AuthenticationPrincipal.errorOnInvalidType()为true时, 将抛出ClassCastException。

这个简单的代码对我有用:

@RestController
public class ApiController {

  @RequestMapping(method = RequestMethod.GET, path = "/api/public/{id}")
  public ResponseEntity<ApiResponse> getPublicResource(
      @PathVariable Integer id,
      @AuthenticationPrincipal String principal
  ) {

    if (id % 2 == 0) {
      SecurityContext context = SecurityContextHolder.getContext();
      Authentication authentication = context.getAuthentication();
      return ResponseEntity.ok(new ApiResponse(
          authentication.getPrincipal().toString(),
          "this method scrapes principal directly from SpringSecurityContext"));
    }

    return ResponseEntity.ok(new ApiResponse(
        principal.toString(),
        "this method retrieves principal from method argument"));

  }

}

只需检查Authentication对象中的主体类型。

答案 4 :(得分:0)

我找到了另一个解决方案,即使这不是规范的解决方案。

在您的控制器中

@RequestMapping(value = "/login", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public ResponseEntity<UserDTO> login(@RequestBody UserDTO user){
    try {
        usersService.authenticate(user.getUsername(), user.getPassword());
        return new ResponseEntity<UserDTO>(user, HttpStatus.OK);
    }
    catch (BadCredentialsException e){
        return new ResponseEntity<UserDTO>(HttpStatus.UNAUTHORIZED);
    }
}

UserDTO只是一个包含用户名和密码的表单

在您的CustomUserDetailsS​​ervice

public void authenticate(String username, String password){
    try{
        User user = new User(username, password);
        Authentication request = new UsernamePasswordAuthenticationToken(user, password, Arrays.asList(WebSecurityConfiguration.USER));
        Authentication result = authenticationManager.authenticate(request);
        SecurityContextHolder.getContext().setAuthentication(result);       
    } catch (InternalAuthenticationServiceException e){
        // treat as a bad credential
    }
}

实施您自己的AuthenticationManager

@Component
class DefaultAuthenticationManager implements AuthenticationManager {

@Autowired
private CustomUserDetailsService usersService;

public Authentication authenticate(Authentication auth) throws AuthenticationException {
    UserDetails user = usersService.loadUserByUsername(((User)auth.getPrincipal()).getUsername());
    if (user != null) {
        return new UsernamePasswordAuthenticationToken(user, user.getPassword(), user.getAuthorities());
    }
// Handle bad credentials here  
}
}

基本的是Principal中的CustomUserDetailsService#authenticate是一个对象,而不是经过身份验证的用户的名称,因此框架可以处理它,然后通过@AuthenticationPrincipal机制注入。这对我有用。

答案 5 :(得分:0)

我面临与你完全相同的问题。 由于某种原因,spring security的@EnableWebSecurity不会自动添加argumentResolver,你需要手动添加它:

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

答案 6 :(得分:0)

您需要检查是否正在使用@AuthenticationPrincipal注释的正确版本以及AuthenticationPrincipalArgumentResolver类的正确版本。

在Spring版本的4.0版本之前,您必须使用类:

  • org.springframework.security.web.bind.annotation.AuthenticationPrincipal

  • org.springframework.security.web.bind.support.AuthenticationPrincipalArgumentResolver

从版本4.0开始,您必须使用:

  • org.springframework.security.core.annotation.AuthenticationPrincipal

  • org.springframework.security.web.method.annotation.AuthenticationPrincipalArgumentResolver

查看@AuthenticationPrincipal官方文档以了解配置示例。

答案 7 :(得分:0)

我通过扩展AbstractUserDetailsAuthenticationProvider的身份验证提供程序实现了这一目标。

在我的提供者中,我重写了以下方法:

受保护的UserDetails检索用户(          字符串用户名,          UsernamePasswordAuthenticationToken身份验证)抛出AuthenticationException {}

,在这种方法中,我将创建我的UserDetails实现并返回它。然后,我可以使用AuthenticationPrincipal批注在控制器中获取它。

答案 8 :(得分:0)

即使文档中提到了有关customUser的内容,但我认为这是不可能的。将@AuthenticationPrincipaljava.lang.Objectorg.springframework.security.core.userdetails.User一起用作弹簧控制器方法参数类型。

  

如果类型不匹配,将返回null,除非   AuthenticationPrincipal.errorOnInvalidType()为true时,   将抛出ClassCastException。   Spring API Documentation