복붙노트

[SPRING] 스프링 보안으로 사용자 업데이트시 권한을 다시로드하는 방법

SPRING

스프링 보안으로 사용자 업데이트시 권한을 다시로드하는 방법

나는 Spring Security를 ​​사용하는 OpenID에 의한 인증으로 응용 프로그램을 만들고있다. 사용자가 로그인하면 일부 권한이 세션에로드됩니다.

다른 사용자의 권한 (권한 취소, 역할 추가)을 수정할 수있는 권한이있는 사용자가 있습니다. 내 질문은 동적으로 사용자 세션 권한을 변경하는 방법입니다. (다른 사용자 세션을 변경하려고하기 때문에 SecurityContextHolder를 사용할 수 없습니다).

간단한 방법 : 사용자 세션을 무효화하는 방법은 무엇입니까? 더 나은 방법 : 새 권한으로 사용자 세션을 새로 고치지 만 방법은 무엇입니까?

해결법

  1. ==============================

    1.어떤 이유로 든 로그인 한 사용자의 권한을 동적으로 업데이트해야 할 경우 (물론 변경된 경우) 로그 아웃하고 로그인 할 필요없이 Spring SecurityContextHolder의 Authentication 객체 (보안 토큰)를 재설정하면됩니다.

    어떤 이유로 든 로그인 한 사용자의 권한을 동적으로 업데이트해야 할 경우 (물론 변경된 경우) 로그 아웃하고 로그인 할 필요없이 Spring SecurityContextHolder의 Authentication 객체 (보안 토큰)를 재설정하면됩니다.

    예:

    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);
    
  2. ==============================

    2.고마워, 많이 도와 줘! SessionRegistry를 사용하면 getAllPrincipals ()를 사용하여 세션의 현재 활성 사용자와 수정하려는 사용자를 비교할 수 있습니다. 세션이 존재하는 경우, 다음을 사용하여 그의 세션을 무효화 할 수 있습니다 : expireNow () (SessionInformation에서) 재 인증을 강요합니다.

    고마워, 많이 도와 줘! SessionRegistry를 사용하면 getAllPrincipals ()를 사용하여 세션의 현재 활성 사용자와 수정하려는 사용자를 비교할 수 있습니다. 세션이 존재하는 경우, 다음을 사용하여 그의 세션을 무효화 할 수 있습니다 : expireNow () (SessionInformation에서) 재 인증을 강요합니다.

    하지만 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
                    }
                }
            }
        }
    } 
    
  3. ==============================

    3.요점 - 사용자 SecurityContexts에 액세스 할 수 있어야합니다.

    요점 - 사용자 SecurityContexts에 액세스 할 수 있어야합니다.

    서블릿 환경에 있고 securityContextPersistenceFilter에서 securityContextRepository로 HttpSession을 사용하는 경우 Spring의 SessionRegistry를 사용하여 수행 할 수 있습니다. 강제로 사용자를 다시 인증하려면 (무의미한 사용 권한 해지보다 좋음) HttpSession을 무효화하십시오. web.xml에 HttpSessionEventPublisher를 추가하는 것을 잊지 마십시오.

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

    스레드 로컬 securityContextRepository를 사용하는 경우 springSecurityFilterChain에 사용자 정의 필터를 추가하여 SecurityContexts 레지스트리를 관리해야합니다. 이를 수행하려면 plain-bean springSecurityFilterChain 구성 (보안 네임 스페이스 단축키 사용 안 함)을 사용해야합니다. 사용자 정의 필터를 사용하는 일반 bean 구성의 경우 인증 및 권한 부여를 완전히 제어 할 수 있습니다.

    일부 링크는 문제 (OpenID 없음)를 정확히 풀지는 못하지만 유용 할 수 있습니다.

  4. ==============================

    4.다른 사용자의 권한을 다시 인증하도록 강요하지 않으면 서 다른 사용자의 권한을 갱신하는 방법을 아직 찾고있는 사용자는 인증을 다시로드하는 인터셉터를 추가 할 수 있습니다. 그러면 권한이 항상 갱신됩니다.

    다른 사용자의 권한을 다시 인증하도록 강요하지 않으면 서 다른 사용자의 권한을 갱신하는 방법을 아직 찾고있는 사용자는 인증을 다시로드하는 인터셉터를 추가 할 수 있습니다. 그러면 권한이 항상 갱신됩니다.

    그러나 여분의 인터셉터로 인해 약간의 성능 영향이 있습니다 (예 : 데이터베이스에서 사용자 역할을 얻는 경우 모든 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("/**");
        }
    
    }
    

    나는 이것에 관한 기사도 만들었다.

  5. ==============================

    5.TwiN에서 주어진 대답을 사용하지만 성능 영향을 줄이기 위해 제어 변수 (users_to_update_roles)를 만듭니다.

    TwiN에서 주어진 대답을 사용하지만 성능 영향을 줄이기 위해 제어 변수 (users_to_update_roles)를 만듭니다.

    @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;
    }
    
  6. from https://stackoverflow.com/questions/9910252/how-to-reload-authorities-on-user-update-with-spring-security by cc-by-sa and MIT license