[SPRING] Spring OAuth2.0 : 클라이언트 ID를 기반으로 사용자 역할 가져 오기
SPRINGSpring OAuth2.0 : 클라이언트 ID를 기반으로 사용자 역할 가져 오기
내 oauth2 인증 서버에 등록 된 클라이언트가 여러 개 있습니다. user1이 ROLE_A, ROLE_B와 같은 역할을 가지고 있다고 가정 해 봅시다. 동일한 사용자는 ROLE_C, ROLE_D와 같은 ROLL을 가지고 있습니다. 이제 사용자가 client1 또는 client2를 사용하여 로그인 할 때 그는 4 가지 역할 모두를 볼 수 있습니다. ROLE_A, ROLE_B, ROLE_C 및 ROLE_D.
내 요구 사항은 user1이 client1에 로그인 할 때 ROLE_A 및 ROLE_B 역할 만 리턴해야한다는 것입니다. client2를 사용하여 로그인하면 ROLE_C 및 ROLE_D 만 반환해야합니다.
이를 달성하기 위해, 내가 계획 한 것은 authenticate 함수 내에 있으며, 나는 clientId를 얻을 필요가있다. 그래서 clientId와 username을 사용하여 db (client-user-roles-mapping 테이블)에서 사용자에게 할당 된 해당 역할을 찾을 수 있습니다. 하지만 문제는 authenticate 함수 내에서 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;
}
}
해결법
-
==============================
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.POJO는 사용자 이름과 암호뿐만 아니라 클라이언트 식별자도 보유해야합니다.
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; } }
클라이언트 식별자가 사용자 이름과 암호 외에도 인증 코드로 전달되도록 인증 프로세스를 조정해야합니다.
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 { } }
<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); ... }
-
==============================
3.요청에서 추가 매개 변수에 액세스하기를 원하기 때문에 CustomAuthenticationProvider 클래스에서 다음을 시도해 볼 수 있습니다
요청에서 추가 매개 변수에 액세스하기를 원하기 때문에 CustomAuthenticationProvider 클래스에서 다음을 시도해 볼 수 있습니다
@Autowired private HttpServletRequest request;
다음 논리를 추가하여 http 요청 매개 변수를 읽고 논리를 추가하여 인증 키에 액세스하십시오.
@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);
from https://stackoverflow.com/questions/49229551/spring-oauth2-0-getting-user-roles-based-on-client-id by cc-by-sa and MIT license