Как перезагрузить права на обновление пользователя с помощью Spring Security - PullRequest
37 голосов
/ 28 марта 2012

Я делаю приложение с аутентификацией по OpenID, используя Spring Security.Когда пользователь вошел в систему, некоторые полномочия загружаются в его сеанс.

У меня есть Пользователь с полным правом, который может изменять полномочия (отзывать, добавлять роли) других пользователей.У меня вопрос, как динамически изменять полномочия сеанса пользователя?(невозможно использовать SecurityContextHolder , потому что я хочу изменить другой сеанс пользователя).

Простой способ: аннулировать сеанс пользователя, но как это сделать?Лучший способ: обновить сеанс пользователя новыми полномочиями, но как?

Ответы [ 5 ]

41 голосов
/ 05 июня 2015

Если вам нужно динамически обновлять права доступа вошедшего в систему пользователя (когда они изменились по какой-либо причине), без необходимости выходить из системы и входить в систему, конечно, вам просто нужно сбросить объект 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);
12 голосов
/ 29 марта 2012

Спасибо, очень помогите мне! С 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
                }
            }
        }
    }
} 
7 голосов
/ 28 марта 2012

Ключевой момент - вы должны иметь доступ к пользователям SecurityContext с.

Если вы находитесь в среде сервлета и используете HttpSession в качестве securityContextRepository в вашем securityContextPersistenceFilter, то это можно сделать с помощью пружины SessionRegistry.Чтобы заставить пользователя повторно авторизоваться (это должно быть лучше, чем аннулирование разрешений без вывода сообщений), аннулируйте его HttpSession.Не забудьте добавить HttpSessionEventPublisher в web.xml

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

Если вы используете локальный поток securityContextRepository, то вы должны добавить пользовательский фильтр в springSecurityFilterChain для управления реестром SecurityContext s.,Для этого вы должны использовать конфигурацию plain-bean springSecurityFilterChain (без ярлыков security namespace).С настройкой обычного компонента с настраиваемыми фильтрами вы будете иметь полный контроль над аутентификацией и авторизацией.

Некоторые ссылки не решают точно вашу проблему (без OpenID), но могут быть полезны:

3 голосов
/ 20 июня 2018

Если кто-то все еще изучает, как обновить полномочия другого пользователя, не заставляя этого пользователя повторно проходить аутентификацию, вы можете попытаться добавить перехватчик, который перезагружает аутентификацию. Это обеспечит постоянное обновление ваших полномочий.

Однако - из-за дополнительного перехватчика произойдет некоторое снижение производительности (например, если вы получите свои пользовательские роли из вашей базы данных, она будет запрашиваться для каждого 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("/**");
    }

}

Я также сделал статью об этом .

0 голосов
/ 20 ноября 2018

Я использую ответ, данный 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;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...