java - j_spring_security_check - spring security tutorial
Cómo recargar las autoridades en la actualización del usuario con Spring Security (4)
Estoy haciendo una aplicación con autenticación por OpenID usando Spring Security. Cuando el usuario ha iniciado sesión, algunas autoridades se cargan en su sesión.
Tengo un usuario con pleno derecho que puede modificar las autorizaciones (revocar, agregar roles) de otros usuarios. Mi pregunta es, ¿cómo cambiar las autoridades de sesión de usuario dinámicamente? (No puedo usar SecurityContextHolder porque quiero cambiar otra sesión de usuario).
Forma simple: invalidar sesión de usuario, pero ¿cómo? Mejor manera: actualizar sesión de usuario con nuevas autoridades, pero ¿cómo?
El punto clave: debe poder acceder a los usuarios de SecurityContext
s.
Si está en el entorno de servlet y está utilizando HttpSession
como securityContextRepository
en su securityContextPersistenceFilter
, entonces puede hacerse con SessionRegistry
de primavera. Para obligar al usuario a volver a realizar la autenticación (debería ser mejor que la revocación de permisos silenciosos) invalide su HttpSession
. No olvide agregar HttpSessionEventPublisher
a web.xml
<listener>
<listener-class>
org.springframework.security.web.session.HttpSessionEventPublisher
</listener-class>
</listener>
Si está usando thread-local securityContextRepository
, entonces debe agregar un filtro personalizado a springSecurityFilterChain
para administrar el registro de SecurityContext
. Para hacer esto, debe usar la configuración springSecurityFilterChain
(sin acceso directo de espacio de nombres). Con la configuración de beans simples con filtros personalizados, tendrá un control total sobre la autenticación y la autorización.
Algunos enlaces no resuelven exactamente su problema (no OpenID), pero pueden ser útiles:
- Registro de sesión NIH para entorno servlet
- Es el ejemplo de trabajo de configuración de primavera de frijol simple
- configuración de spring de beans reales para X.509 auth , puede comenzar con ella y modificarla para usar OpenID en lugar de X.509.
Gracias, ayúdame mucho! Con SessionRegistry
, puedo usar getAllPrincipals() para comparar al usuario para modificarlo con los usuarios activos actuales en las sesiones. Si existe una sesión, puedo invalidar su sesión usando: expireNow() (de SessionInformation
) para forzar la re-autenticación.
¿Pero no entiendo la utilidad de securityContextPersistenceFilter
?
EDITAR:
// 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
}
}
}
}
}
Si alguien sigue investigando cómo actualizar las autorizaciones de otro usuario sin obligar a ese usuario a volver a autenticarse, puede intentar agregar un interceptor que vuelva a cargar la autenticación. Esto asegurará que sus autoridades estén siempre actualizadas.
Sin embargo, debido al interceptor adicional, habrá algunos impactos en el rendimiento (por ejemplo, si obtiene sus roles de usuario de su base de datos, se consultará para cada solicitud 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;
}
}
Esta implementación específica utiliza OAuth2 ( OAuth2AuthenticationToken
), pero puede usar UsernamePasswordAuthenticationToken
lugar.
Y ahora, para agregar su interceptor a la configuración:
@Configuration
public class WebConfiguration extends WebMvcConfigurationSupport {
@Autowired
private VerifyAccessInterceptor verifyAccessInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(verifyAccessInterceptor).addPathPatterns("/**");
}
}
Si necesita actualizar dinámicamente las autorizaciones de un usuario conectado (cuando estas han cambiado, por cualquier motivo), sin tener que cerrar sesión e iniciar sesión, solo tiene que restablecer el objeto de Authentication
(token de seguridad) en Spring SecurityContextHolder
.
Ejemplo:
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);