Spring OAuth2.0: Getting User Roles based on Client Id

☆樱花仙子☆ 提交于 2019-11-27 14:54:22

Here is you code after modification

@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 "";
}

more info about RequestContextHolder

Extend UsernamePasswordAuthenticationToken


A POJO is required to hold not just the username and the password but also the client identifier.

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; }
}

Extend UsernamePasswordAuthenticationFilter


The authentication process needs to be tweaked so that the client identifier is passed to the authentication code in addition to the username and password.

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);
  }
}

Use the extra information available for logging in


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 {
  }
}

Force Spring Security to use the custom filter


<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>

or, if using Java configuration:

@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);
      ...
}

For your requirement, since you want to just access additional parameters from the request, you could try out the following in your CustomAuthenticationProvider class

@Autowired
    private HttpServletRequest request;

Add the following logic to read the httpRequest parameters and add your logic to access the authorization key

@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));
   }
}

Now, you will have the encode Basic Authentication field which you can decode like the one below

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);
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!