Spring OAuth2.0:根据客户端 ID 获取用户角色
Posted
技术标签:
【中文标题】Spring OAuth2.0:根据客户端 ID 获取用户角色【英文标题】:Spring OAuth2.0: Getting User Roles based on Client Id 【发布时间】:2018-08-20 03:18:38 【问题描述】:我为我的 oauth2 身份验证服务器注册了多个客户端。假设 user1 有诸如ROLE_A
、ROLE_B
用于 client1 的角色,同一用户有诸如 ROLE_C
、ROLE_D
用于 client2。现在,当用户使用 client1 或 client2 登录时,他能够看到所有四个角色,即。 ROLE_A
、ROLE_B
、ROLE_C
和 ROLE_D
。
我的要求是当 user1 登录到 client1 时,它应该只返回角色 ROLE_A
和 ROLE_B
。当他使用 client2 登录时,它应该只返回 ROLE_C
和 ROLE_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 嘿,这很酷.....工作正常.... 【参考方案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);
【讨论】:
以上是关于Spring OAuth2.0:根据客户端 ID 获取用户角色的主要内容,如果未能解决你的问题,请参考以下文章
Spring OAuth2.0 - 动态注册 OAuth2.0 客户端
Spring Security实现OAuth2.0——资源服务
Spring Security 入门(1-3)Spring Security oauth2.0 指南
Spring Cloud云架构 - SSO单点登录之OAuth2.0 根据token获取用户信息