【问题标题】:Spring OAuth2.0: Getting User Roles based on Client IdSpring OAuth2.0:根据客户端 ID 获取用户角色
【发布时间】:2018-08-20 03:18:38
【问题描述】:

我为我的 oauth2 身份验证服务器注册了多个客户端。假设 user1 有诸如ROLE_AROLE_B 用于 client1 的角色,同一用户有诸如 ROLE_CROLE_D 用于 client2。现在,当用户使用 client1client2 登录时,他能够看到所有四个角色,即。 ROLE_AROLE_BROLE_CROLE_D

我的要求是当 user1 登录到 client1 时,它应该只返回角色 ROLE_AROLE_B。当他使用 client2 登录时,它应该只返回 ROLE_CROLE_D

为了实现这一点,我计划在身份验证功能内,我需要获取 clientId。所以使用clientId和用户名我可以从db(client-user-roles-mapping table)中找到分配给用户的相应角色。 .但问题是我不知道如何在身份验证函数中获取 clientId

 @Override
    public Authentication authenticate(final Authentication authentication) throws AuthenticationException {
        String userName = ((String) authentication.getPrincipal()).toLowerCase();
        String password = (String) authentication.getCredentials();
        if (userName != null && authentication.getCredentials() != null) {
                String clientId = // HERE HOW TO GET THE CLIENT ID 
                Set<String> userRoles = authRepository.getUserRoleDetails(userName.toLowerCase(), clientId);
                Collection<SimpleGrantedAuthority> authorities = fillUserAuthorities(userRoles);
                Authentication token =  new UsernamePasswordAuthenticationToken(userName, StringUtils.EMPTY, authorities);
                return token;
            } else {
                throw new BadCredentialsException("Authentication Failed!!!");
            }
         } else {
             throw new BadCredentialsException("Username or Password cannot be empty!!!");
         }         
    }

谁能帮我解决这个问题

更新 1

CustomAuthenticationProvider.java

@Component
public class CustomAuthenticationProvider implements AuthenticationProvider {

    private final Logger log = LoggerFactory.getLogger(getClass());

    @Autowired
    private LDAPAuthenticationProvider ldapAuthentication;

    @Autowired
    private AuthRepository authRepository;

    public CustomAuthenticationProvider() {
        super();
    }

    @Override
        public Authentication authenticate(final Authentication authentication) throws AuthenticationException {
            String userName = ((String) authentication.getPrincipal()).toLowerCase();
            String password = (String) authentication.getCredentials();
            if (userName != null && authentication.getCredentials() != null) {
                    String clientId = // HERE HOW TO GET THE CLIENT ID 
                    Set<String> userRoles = authRepository.getUserRoleDetails(userName.toLowerCase(), clientId);
                    Collection<SimpleGrantedAuthority> authorities = fillUserAuthorities(userRoles);
                    Authentication token =  new UsernamePasswordAuthenticationToken(userName, StringUtils.EMPTY, authorities);
                    return token;
                } else {
                    throw new BadCredentialsException("Authentication Failed!!!");
                }
             } else {
                 throw new BadCredentialsException("Username or Password cannot be empty!!!");
             }         
    }


    public boolean invokeAuthentication(String username, String password, Boolean isClientValidation) {
        try {
            Map<String, Object> userDetails = ldapAuthentication.authenticateUser(username, password);
            if(Boolean.parseBoolean(userDetails.get("success").toString())) {
                return true;
            }
        } catch (Exception exception) {
            log.error("Exception in invokeAuthentication::: " + exception.getMessage());
        }
        return false;
    }

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

    private Collection<SimpleGrantedAuthority> fillUserAuthorities(Set<String> roles) {
        Collection<SimpleGrantedAuthority> authorties = new ArrayList<SimpleGrantedAuthority>();
        for(String role : roles) {
            authorties.add(new SimpleGrantedAuthority(role));
        }
        return authorties;
    }
}

【问题讨论】:

  • 你可以在 spring 中使用 HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();在代码的任何部分,然后获取授权标头并对其进行解码,然后您将拥有 clientId
  • @Shadi 嘿,这很酷.....工作正常....

标签: java spring spring-security oauth-2.0 spring-security-oauth2


【解决方案1】:

这是你修改后的代码

@Override
public Authentication authenticate(final Authentication authentication) throws AuthenticationException {
    String userName = ((String) authentication.getPrincipal()).toLowerCase();
    String password = (String) authentication.getCredentials();
    if (userName != null && authentication.getCredentials() != null) {
            String clientId = getClientId();
            // validate client ID before use
            Set<String> userRoles = authRepository.getUserRoleDetails(userName.toLowerCase(), clientId);
            Collection<SimpleGrantedAuthority> authorities = fillUserAuthorities(userRoles);
            Authentication token =  new UsernamePasswordAuthenticationToken(userName, StringUtils.EMPTY, authorities);
            return token;
        } else {
            throw new BadCredentialsException("Authentication Failed!!!");
        }
     } else {
         throw new BadCredentialsException("Username or Password cannot be empty!!!");
     }         


private  String getClientId(){
    final HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();

    final String authorizationHeaderValue = request.getHeader("Authorization");
    final String base64AuthorizationHeader = Optional.ofNullable(authorizationHeaderValue)
            .map(headerValue->headerValue.substring("Basic ".length())).orElse("");

    if(StringUtils.isNotEmpty(base64AuthorizationHeader)){
        String decodedAuthorizationHeader = new String(Base64.getDecoder().decode(base64AuthorizationHeader), Charset.forName("UTF-8"));
        return decodedAuthorizationHeader.split(":")[0];
    }

    return "";
}

更多关于RequestContextHolder的信息

【讨论】:

    【解决方案2】:

    扩展UsernamePasswordAuthenticationToken


    POJO 不仅需要保存用户名和密码,还需要保存客户端标识符。

    public ExtendedUsernamePasswordAuthenticationToken extends UsernamePasswordAuthenticationToken {
      private final String clientId;
    
      public ExtendedUsernamePasswordAuthenticationToken(Object principal
                                                        , Object credentials
                                                        , String clientId) {
        super(principal, credentials);
    
        this.clientId = clientId;
      }
    
      public String getClientId() { return clientId; }
    }
    

    扩展UsernamePasswordAuthenticationFilter


    需要对身份验证过程进行调整,以便除了用户名和密码之外,还将客户端标识符传递给身份验证代码。

    public class ExtendedUsernamePasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
      public ExtendedUsernamePasswordAuthenticationFilter () { super(); }
    
      @Override
      public public Authentication attemptAuthentication(HttpServletRequest request
                                                        , HttpServletResponse response)
                                                        throws AuthenticationException {
        // See the source code of UsernamePasswordAuthenticationFilter
        // to implement this. Instead of creating an instance of
        // UsernamePasswordAuthenticationToken, create an instance of
        // ExtendedUsernamePasswordAuthenticationToken, something along
        // the lines of:
    
        final String username = obtainUsername(request);
        final String password = obtainPassword(request);
        final String clientId = obtainClientId(request);
    
        ...
    
        final Authentication authentication = new ExtendedUsernamePasswordAuthenticationToken(username, password, clientId);
    
        return getAuthenticationManager().authenticate(authentication);
      }
    }
    

    使用可用于登录的额外信息


    public CustomAuthenticationProvider implements AuthenticationProvider {
      ...
    
      @Override
      public boolean supports(final Class<?> authentication) {
        return authentication.isAssignableFrom(ExtendedUsernamePasswordAuthenticationToken.class);
      }
    
    
      @Override
      public Authentication authenticate(final Authentication authentication)
                                         throws AuthenticationException {
      }
    }
    

    强制 Spring Security 使用自定义过滤器


    <bean class="com.path.to.filter.ExtendedUsernamePasswordAuthenticationFilter" id="formAuthenticationFilter">
      <property name="authenticationManager" ref="authenticationManager"/>
    </bean>
    
    <http ... >
      <security:custom-filter position="FORM_LOGIN_FILTER" ref="formAuthenticationFilter"/>
    
      ...
    </http>
    

    或者,如果使用 Java 配置:

    @Bean
    public ExtendedUsernamePasswordAuthenticationFilter usernamePasswordAuthenticationFilter(final AuthenticationManager authenticationManager) {
      final ExtendedUsernamePasswordAuthenticationFilter filter = new ExtendedUsernamePasswordAuthenticationFilter();
    
      filter.setAuthenticationManager(authenticationManager);
    
      return filter;
    }
    
    protected void configure(HttpSecurity http) throws Exception {
      http.addFilterAt(usernamePasswordAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
          ...
    }
    

    【讨论】:

    • 感谢 Manish 的回复,您能否根据扩展 UsernamePasswordAuthenticationFilter 的位置和Force Spring Security to use the custom filter 的相关 java 配置给我一些信息
    • usernamePasswordAuthenticationFilter 函数没有参数。 protected void configure(HttpSecurity http) throws Exception { http.addFilterAt(usernamePasswordAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class); ... }
    • @AlexMan, WebSecurityConfigurerAdapter 有一个 authenticationManager 方法,它返回当前正在使用的 AuthenticationManager。答案中提供了解决方案的大致轮廓。您必须通过自己的努力将其余部分组合在一起,而不是期望用户为您编写整个代码以避免help vampire tendencies
    【解决方案3】:

    根据您的要求,由于您只想从请求中访问其他参数,您可以在 CustomAuthenticationProvider 类中尝试以下内容

    @Autowired
        private HttpServletRequest request;
    

    添加以下逻辑以读取 httpRequest 参数并添加您的逻辑以访问授权密钥

    @Override
        public Authentication authenticate(Authentication authentication) throws AuthenticationException {
    
        Enumeration<String> headerNames = request.getHeaderNames();
        while(headerNames.hasMoreElements()) {
            String headerName = headerNames.nextElement();
            System.out.println("Header Name - " + headerName + ", Value - " + request.getHeader(headerName));
       }
    }
    

    现在,您将拥有编码基本身份验证字段,您可以像下面这样解码

    if (authorization != null && authorization.startsWith("Basic")) {
            // Authorization: Basic base64credentials
            String base64Credentials = authorization.substring("Basic".length()).trim();
            String credentials = new String(Base64.getDecoder().decode(base64Credentials),
                    Charset.forName("UTF-8"));
            // client/secret = clientId:secret
            final String[] values = credentials.split(":",2);
    

    【讨论】:

      猜你喜欢
      • 2019-06-16
      • 2019-10-13
      • 2020-09-19
      • 2014-10-22
      • 2016-06-17
      • 2020-08-25
      • 2018-11-09
      • 1970-01-01
      相关资源
      最近更新 更多