programing

Spring Security로 사용자 업데이트 시 권한을 다시 로드하는 방법

muds 2023. 11. 2. 22:09
반응형

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("/**");
    }

}

저도 이에 대한 기사를 만들었습니다.

핵심 사항 - 사용자에게 접근할 수 있어야 합니다.SecurityContexts.

서블릿 환경에서 사용하는 경우HttpSession~하듯이securityContextRepository당신의securityContextPersistenceFilter, 스프링으로 할 수 있을 겁니다SessionRegistry. 사용자에게 재인증을 강요하려면(무음 권한 취소보다 낫아야 함) 사용자의 권한을 무효화합니다.HttpSession. 추가하는 것을 잊지 마세요.HttpSessionEventPublisherweb.xml로

<listener>
    <listener-class>
        org.springframework.security.web.session.HttpSessionEventPublisher
    </listener-class>
</listener>

스레드-로컬을 사용하는 경우securityContextRepository, 사용자 정의 필터를 추가해야 합니다.springSecurityFilterChain관리하다SecurityContext등록부이렇게 하려면 플레인빈을 사용해야 합니다.springSecurityFilterChain구성(없음)securitynamespace 단축키)사용자 지정 필터가 있는 플레인 빈 구성을 사용하면 인증 및 권한을 완전히 제어할 수 있습니다.

일부 링크는 사용자의 문제를 정확하게 해결하지 못합니다(No Open).ID), 하지만 유용할 수 있습니다.

저는 위의 매우 구체적인 사례가 있는데, 저는 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

반응형