Spring Security로 사용자 업데이트 시 권한을 다시 로드하는 방법
오픈 인증으로 애플리케이션을 하고 있습니다.Spring Security를 사용한 ID.사용자가 로그인하면 세션에 일부 권한이 로드됩니다.
다른 사용자의 권한(취소, 역할 추가)을 수정할 수 있는 전체 권한을 가진 사용자가 있습니다.제 질문은 사용자 세션 권한을 동적으로 변경하는 방법입니다(다른 사용자 세션을 변경하려면 SecurityContextHolder를 사용할 수 없음).
간단한 방법: 사용자 세션을 무효화하지만 어떻게 해야 합니까? 더 나은 방법: 새 권한으로 사용자 세션을 새로 고칩니다. 그러나 어떻게 해야 합니까?
로그인한 사용자 권한을 동적으로 업데이트해야 하는 경우(이러한 권한이 변경된 경우), 물론 로그아웃하고 로그인할 필요가 없습니다.Authentication
봄에 개체(보안 토큰)SecurityContextHolder
.
예:
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
List<GrantedAuthority> updatedAuthorities = new ArrayList<>(auth.getAuthorities());
updatedAuthorities.add(...); //add your role here [e.g., new SimpleGrantedAuthority("ROLE_NEW_ROLE")]
Authentication newAuth = new UsernamePasswordAuthenticationToken(auth.getPrincipal(), auth.getCredentials(), updatedAuthorities);
SecurityContextHolder.getContext().setAuthentication(newAuth);
감사합니다. 많이 도와주세요.SessionRegistry
, getAllPrincipals()를 사용하여 세션에서 수정할 사용자와 현재 활성 사용자를 비교할 수 있습니다.세션이 존재하는 경우 다음을 사용하여 세션을 무효화할 수 있습니다. expireNow()(from)SessionInformation
재authentic화를 강제합니다.
하지만 저는 그가 얼마나 유용한지 이해할 수 없습니다.securityContextPersistenceFilter
?
편집:
// user object = User currently updated
// invalidate user session
List<Object> loggedUsers = sessionRegistry.getAllPrincipals();
for (Object principal : loggedUsers) {
if(principal instanceof User) {
final User loggedUser = (User) principal;
if(user.getUsername().equals(loggedUser.getUsername())) {
List<SessionInformation> sessionsInfo = sessionRegistry.getAllSessions(principal, false);
if(null != sessionsInfo && sessionsInfo.size() > 0) {
for (SessionInformation sessionInformation : sessionsInfo) {
LOGGER.info("Exprire now :" + sessionInformation.getSessionId());
sessionInformation.expireNow();
sessionRegistry.removeSessionInformation(sessionInformation.getSessionId());
// User is not forced to re-logging
}
}
}
}
}
다른 사용자에게 재인증을 강요하지 않고 다른 사용자의 권한을 업데이트하는 방법을 여전히 찾고 있는 사람이 있다면 인증을 다시 로드하는 가로채기를 추가할 수 있습니다.이렇게 하면 권한이 항상 업데이트됩니다.
그러나 추가 인터셉터로 인해 성능에 일부 영향이 있습니다(예: 데이터베이스에서 사용자 역할을 가져온 경우 모든 HTTP 요청에 대해 쿼리됩니다).
@Component
public class VerifyAccessInterceptor implements HandlerInterceptor {
// ...
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
Set<GrantedAuthority> authorities = new HashSet<>();
if (auth.isAuthenticated()) {
authorities.add(new SimpleGrantedAuthority("ROLE_USER"));
}
User userFromDatabase = getUserFromDatabase(auth.getName());
if (userFromDatabase != null) {
// add whatever authorities you want here
authorities.add(new SimpleGrantedAuthority("..."));
}
Authentication newAuth = null;
if (auth.getClass() == OAuth2AuthenticationToken.class) {
OAuth2User principal = ((OAuth2AuthenticationToken)auth).getPrincipal();
if (principal != null) {
newAuth = new OAuth2AuthenticationToken(principal, authorities,(((OAuth2AuthenticationToken)auth).getAuthorizedClientRegistrationId()));
}
}
SecurityContextHolder.getContext().setAuthentication(newAuth);
return true;
}
}
이 특정 구현은 OAuth2를 사용합니다(OAuth2AuthenticationToken
), 그러나 사용할 수 있습니다.UsernamePasswordAuthenticationToken
대신.
이제 인터셉터를 구성에 추가하려면 다음을 수행합니다.
@Configuration
public class WebConfiguration extends WebMvcConfigurationSupport {
@Autowired
private VerifyAccessInterceptor verifyAccessInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(verifyAccessInterceptor).addPathPatterns("/**");
}
}
저도 이에 대한 기사를 만들었습니다.
핵심 사항 - 사용자에게 접근할 수 있어야 합니다.SecurityContext
s.
서블릿 환경에서 사용하는 경우HttpSession
~하듯이securityContextRepository
당신의securityContextPersistenceFilter
, 스프링으로 할 수 있을 겁니다SessionRegistry
. 사용자에게 재인증을 강요하려면(무음 권한 취소보다 낫아야 함) 사용자의 권한을 무효화합니다.HttpSession
. 추가하는 것을 잊지 마세요.HttpSessionEventPublisher
web.xml로
<listener>
<listener-class>
org.springframework.security.web.session.HttpSessionEventPublisher
</listener-class>
</listener>
스레드-로컬을 사용하는 경우securityContextRepository
, 사용자 정의 필터를 추가해야 합니다.springSecurityFilterChain
관리하다SecurityContext
등록부이렇게 하려면 플레인빈을 사용해야 합니다.springSecurityFilterChain
구성(없음)security
namespace 단축키)사용자 지정 필터가 있는 플레인 빈 구성을 사용하면 인증 및 권한을 완전히 제어할 수 있습니다.
일부 링크는 사용자의 문제를 정확하게 해결하지 못합니다(No Open).ID), 하지만 유용할 수 있습니다.
- 서블릿 환경의 NIH 세션 레지스트리
- 플레인-빈 스프링 구성 작업 예시입니다.
- 실생활 플레인빈 스프링 구성 X.509 auth, 당신은 그것으로 시작해서 Open을 사용하도록 수정할 수 있습니다.X.509 대신 신분증.
저는 위의 매우 구체적인 사례가 있는데, 저는 Redis를 사용하여 https://github.com/spring-projects/spring-session 과의 사용자 세션을 추적합니다.그런 다음 관리자가 사용자에게 일부 역할을 추가하면 Redis에서 사용자 세션을 찾아서 대체합니다.principal
그리고.authorities
세션을 저장합니다.
public void updateUserRoles(String username, Set<GrantedAuthority> newRoles) {
if (sessionRepository instanceof FindByIndexNameSessionRepository) {
Map<String, org.springframework.session.Session> map =
((FindByIndexNameSessionRepository<org.springframework.session.Session>) sessionRepository)
.findByPrincipalName(username);
for (org.springframework.session.Session session : map.values()) {
if (!session.isExpired()) {
SecurityContext securityContext = session.getAttribute(SPRING_SECURITY_CONTEXT_KEY);
Authentication authentication = securityContext.getAuthentication();
if (authentication instanceof UsernamePasswordAuthenticationToken) {
Collection<GrantedAuthority> authorities = new HashSet<>(authentication.getAuthorities());
//1. Update of authorities
authorities.addAll(newRoles);
Object principalToUpdate = authentication.getPrincipal();
if (principalToUpdate instanceof User) {
//2. Update of principal: Your User probably extends UserDetails so call here method that update roles to allow
// org.springframework.security.core.userdetails.UserDetails.getAuthorities return updated
// Set of GrantedAuthority
securityContext
.setAuthentication(new UsernamePasswordAuthenticationToken(principalToUpdate, authentication
.getCredentials(), authorities));
session.setAttribute(SPRING_SECURITY_CONTEXT_KEY, securityContext);
sessionRepository.save(session);
}
}
}
}
}
}
TwiN에서 제공하는 답변을 사용하지만 성능에 영향을 줄이기 위해 제어 변수(users_to_update_rolles)를 만듭니다.
@Component
public class RoleCheckInterceptor implements HandlerInterceptor {
public static ArrayList<String> update_role = new ArrayList<>();
@Autowired
private IUser iuser;
public static Set<String> users_to_update_roles = new HashSet<>();
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
try {
CurrentUser current = (CurrentUser) auth.getPrincipal();
String username = current.getUser().getUsername();
if (users_to_update_roles.contains(username)) {
updateRoles(auth, current);
users_to_update_roles.remove(username);
}
} catch (Exception e) {
// TODO: handle exception
}
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
}
private void updateRoles(Authentication auth, CurrentUser current) {
User findOne = iuser.findOne(current.getUser().getUsername());
List<GrantedAuthority> updatedAuthorities = new ArrayList<>();
for (Role role : findOne.getRoles()) {
updatedAuthorities.add(new SimpleGrantedAuthority(role.name()));
}
Authentication newAuth = new UsernamePasswordAuthenticationToken(auth.getPrincipal(), auth.getCredentials(),
updatedAuthorities);
SecurityContextHolder.getContext().setAuthentication(newAuth);
}
}
내 컨트롤러에서 역할이 업데이트된 사용자를 추가합니다.
public ModelAndView roleSave(@PathVariable long numero_documento, Funcionario funcionario) {
ModelAndView modelAndView = new ModelAndView("funcionario/role");
Set<Role> roles = funcionario.getPessoa().getUser().getRoles();
funcionario = funcionarioService.funcionarioNumero_documento(numero_documento);
funcionario.getPessoa().getUser().setRoles(roles);
iUser.save(funcionario.getPessoa().getUser());
RoleCheckInterceptor.users_to_update_roles.add(funcionario.getPessoa().getUser().getUsername());
modelAndView.addObject("funcionario", funcionario);
modelAndView.addObject("sucess", "Permissões modificadas");
return modelAndView;
}
언급URL : https://stackoverflow.com/questions/9910252/how-to-reload-authorities-on-user-update-with-spring-security
'programing' 카테고리의 다른 글
__sync_sync_sync_sync는 어떤 역할을 합니까? (0) | 2023.11.02 |
---|---|
XML 구문 분석 - 요소트리 대 SAX 및 DOM (0) | 2023.11.02 |
자바스크립트에서 Pass by Value and Pass by Reference (0) | 2023.11.02 |
MySQL에서 테이블의 고유 제약 조건을 표시하려면 어떻게 해야 합니까? (0) | 2023.11.02 |
join을 사용하기 위해 이 쿼리를 변경하는 방법은 무엇입니까? (0) | 2023.11.02 |