【问题标题】:How to retrieve custom UserDetails from SecurityContextHolder in jHipster?如何从 jHipster 中的 SecurityContextHolder 检索自定义 UserDetails?
【发布时间】:2019-09-10 16:44:10
【问题描述】:

我是 jHipster 的新手,我正在尝试将用户与公司 ID 相关联,以便根据所述公司 ID 限制他们可以访问的实体。

我希望能够使用 SecurityContext 来存储我自定义的 UserDetails 实现。 我已经遵循了多个教程,但似乎无法使其正常工作:SecurityContext 返回的对象始终是 org.springframework.security.core.userdetails.User 类型,因此无法将其转换为我实现的 UserDetails 类.

DomainUserDetailsS​​ervice.java

@Service("domainUserDetailsService")
public class DomainUserDetailsService implements UserDetailsService {

    private final Logger log = LoggerFactory.getLogger(DomainUserDetailsService.class);

    private  UserRepository userRepository;

    public DomainUserDetailsService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    @Override
    @Transactional(readOnly = true)
    public UserDetails loadUserByUsername(final String login) {
        log.debug("Authenticating {}", login);

        User user = userRepository.findOneWithAuthoritiesByEmail(login).get();

        return new UserDetails(user, 22L);
    }

UserDetails.java

public class UserDetails implements org.springframework.security.core.userdetails.UserDetails {
    private User user;
    private Long companyId = 0L;

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }

    public Long getCompanyId() {
        return companyId;
    }

    public void setCompanyId(Long companyId) {
        this.companyId = companyId;
    }

    public UserDetails(User user, Long companyId) {
        this.user = user;
        this.companyId = companyId;
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return user.getAuthorities().stream().map(authority -> new SimpleGrantedAuthority(authority.getName().toString())).collect(Collectors.toList());
    }

    public Long getId() {
        return user.getId();
    }

    @Override
    public String getPassword() {
        return user.getPassword();
    }

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

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

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

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

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

    public User getUserDetails() {
        return user;
    }
}

SecurityConfiguration.java

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
@Import(SecurityProblemSupport.class)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    private final AuthenticationManagerBuilder authenticationManagerBuilder;

    private final UserDetailsService userDetailsService;

    private final TokenProvider tokenProvider;

    private final CorsFilter corsFilter;

    private final SecurityProblemSupport problemSupport;

    public SecurityConfiguration(AuthenticationManagerBuilder authenticationManagerBuilder, @Qualifier("domainUserDetailsService") UserDetailsService userDetailsService, TokenProvider tokenProvider, CorsFilter corsFilter, SecurityProblemSupport problemSupport) {
        this.authenticationManagerBuilder = authenticationManagerBuilder;
        this.userDetailsService = userDetailsService;
        this.tokenProvider = tokenProvider;
        this.corsFilter = corsFilter;
        this.problemSupport = problemSupport;
    }


    @Override
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

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

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

SecurityUtils.java

public static UserDetails getCurrentUserDetail() {
        SecurityContext securityContext = SecurityContextHolder.getContext();
        Authentication authentication = securityContext.getAuthentication();
        if (authentication != null) {
            if (authentication.getPrincipal() instanceof UserDetails) {
                UserDetails springSecurityUser = (UserDetails) authentication.getPrincipal();
                return springSecurityUser;
            }
        }
        return null;
    }

问题是在 SecurityUtils 中,authentication.getPrincipal().getClass() 的类型总是 org.springframework.security.core.userdetails.User。 我希望它是 UserDetails 类型。

编辑 代码确实通过 DomainUserDetailsS​​ervice,我可以在日志中看到它。

我认为问题与在 UserJWTController.java 中的用户身份验证期间调用的 createToken 期间在 TokenProvider.java 中覆盖的身份验证有关

【问题讨论】:

  • log.debug("Authenticating {}", login); 认证时能找到这个日志吗?你能分享你的 HttpSecurity 吗?
  • 什么是 tokenProvider ?
  • @chaoluo,是的,代码确实通过 log.debug("Authenticating {}", login); TokenProvider 是 jHipster 生成的一个类。这是example of it。通过它,我想我可能在public Authentication getAuthentication(String token) 中找到了我的问题
  • 你能分享你的 HttpSecurtiy 吗?
  • 你试过使用中间表吗?像Employeecompany_iduser_id 作为字段?它对我有用,让我免于许多麻烦

标签: java spring-security jhipster


【解决方案1】:

我遇到了同样的问题,我通过在 TokenProvider.createToken 和 TokenProvider.getAuthentication 方法中进行一些更新来处理它。想法是在 createToken 方法中将租户(在您的情况下为公司)ID 或任何其他变量保存在 JWT 令牌中,然后在 getAuthentication 方法中提取它们。以下是我的方法的代码:

public String createToken(Authentication authentication, boolean rememberMe, String channel) {
        String authorities = authentication.getAuthorities().stream()
            .map(GrantedAuthority::getAuthority)
            .collect(Collectors.joining(","));

        long now = (new Date()).getTime();
        Date validity;
        if (rememberMe) {
            validity = new Date(now + this.tokenValidityInMillisecondsForRememberMe);
        } else {
            validity = new Date(now + this.tokenValidityInMilliseconds);
        }

        Authentication auth = SecurityContextHolder.getContext().getAuthentication();
        Long userPkId = null;
        Long tenantId = null;

        if (auth != null) {
            Object principal = auth.getPrincipal();

            if (principal instanceof DomainUserDetails) {
                DomainUserDetails domainUserDetails = (DomainUserDetails) principal;
                tenantId = domainUserDetails.getTenantId();
                userPkId = domainUserDetails.getId();
            }
        }

        return Jwts.builder()
            .setSubject(authentication.getName())
            .claim(AUTHORITIES_KEY, authorities)
            .claim(USER_ID_KEY, userPkId)
            .claim(TENANT_KEY, tenantId)
            .claim(CHANNEL_KEY, channel)
            .signWith(key, SignatureAlgorithm.HS512)
            .setExpiration(validity)
            .compact();
    }

现在我们可以从 JWT 令牌中获取这些变量

public Authentication getAuthentication(String token) {
        Claims claims = Jwts.parser()
            .setSigningKey(key)
            .parseClaimsJws(token)
            .getBody();

        Collection<? extends GrantedAuthority> authorities =
            Arrays.stream(claims.get(AUTHORITIES_KEY).toString().split(","))
                .map(SimpleGrantedAuthority::new)
                .collect(Collectors.toList());

        Integer userPkId = (Integer) claims.get(USER_ID_KEY);
        Integer tenandId = (Integer)  claims.get(TENANT_KEY);
        String channel = (String) claims.get(CHANNEL_KEY);

        DomainUserDetails principal = new DomainUserDetails(claims.getSubject(), "", authorities, userPkId, tenandId, channel);

        return new UsernamePasswordAuthenticationToken(principal, token, authorities);
    }

【讨论】:

    【解决方案2】:

    您应该覆盖configure(AuthenticationManagerBuilder auth),而不是@Autowired public void configureGlobal

     @Override
        public void configure(AuthenticationManagerBuilder auth) throws Exception {
            auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
        }
    

    【讨论】:

    • 我已经尝试过您的建议,但似乎没有什么不同。即使基于之前的代码,它仍然会通过 DomainUserDetailsS​​ervice,正如我在日志中看到的那样。 @chalou 上面询问了 TokenProvider 类,我相信我找到了问题,但我还不知道如何正确解决它....问题似乎出在方法 public Authentication getAuthentication(String token) 的班级TokenProvider
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-07-24
    • 1970-01-01
    • 1970-01-01
    • 2012-01-29
    • 2015-05-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多