Tengo una aplicación web Spring MVC que usa Spring Security. Quiero saber el nombre de usuario del usuario actualmente conectado. Estoy usando el fragmento de código que figura a continuación. ¿Es esta la forma aceptada?
No me gusta tener una llamada a un método estático dentro de este controlador, que anula todo el propósito de Spring, en mi humilde opinión. ¿Hay alguna forma de configurar la aplicación para que se inyecte el SecurityContext actual o la Autenticación actual?
@RequestMapping(method = RequestMethod.GET)
public ModelAndView showResults(final HttpServletRequest request...) {
final String currentUser = SecurityContextHolder.getContext().getAuthentication().getName();
...
}
java
spring
spring-mvc
spring-security
Scott Bale
fuente
fuente
Respuestas:
Si está utilizando Spring 3 , la forma más fácil es:
fuente
Mucho ha cambiado en el mundo de la primavera desde que se respondió esta pregunta. Spring ha simplificado la obtención del usuario actual en un controlador. Para otros beans, Spring adoptó las sugerencias del autor y simplificó la inyección de 'SecurityContextHolder'. Más detalles están en los comentarios.
Esta es la solución con la que terminé yendo. En lugar de usar
SecurityContextHolder
en mi controlador, quiero inyectar algo que se usaSecurityContextHolder
debajo del capó pero abstrae esa clase de tipo singleton de mi código. No he encontrado otra manera de hacer esto que no sea rodar mi propia interfaz, así:Ahora, mi controlador (o cualquier POJO) se vería así:
Y, debido a que la interfaz es un punto de desacoplamiento, las pruebas unitarias son sencillas. En este ejemplo uso Mockito:
La implementación predeterminada de la interfaz se ve así:
Y, finalmente, la producción Spring config se ve así:
Parece más que un poco tonto que Spring, un contenedor de inyección de dependencia de todas las cosas, no haya proporcionado una forma de inyectar algo similar. Entiendo que
SecurityContextHolder
fue heredado de acegi, pero aún así. El hecho es que están tan cerca: si soloSecurityContextHolder
tuviera un getter para obtener laSecurityContextHolderStrategy
instancia subyacente (que es una interfaz), podría inyectar eso. De hecho, incluso abrí un problema de Jira en ese sentido.Una última cosa: acabo de cambiar sustancialmente la respuesta que tenía aquí antes. Verifique el historial si tiene curiosidad, pero, como me señaló un compañero de trabajo, mi respuesta anterior no funcionaría en un entorno de subprocesos múltiples. El subyacente
SecurityContextHolderStrategy
utilizado porSecurityContextHolder
es, por defecto, una instancia deThreadLocalSecurityContextHolderStrategy
, que almacenaSecurityContext
s en aThreadLocal
. Por lo tanto, no es necesariamente una buena idea inyectarSecurityContext
directamente en un bean en el momento de la inicialización; es posible que deba recuperarse deThreadLocal
cada vez, en un entorno de subprocesos múltiples, de modo que se recupere el correcto.fuente
Estoy de acuerdo en que tener que consultar el SecurityContext para el usuario actual apesta, parece una forma muy poco flexible de manejar este problema.
Escribí una clase estática de "ayuda" para tratar este problema; está sucio porque es un método global y estático, pero pensé de esta manera si cambiamos algo relacionado con la Seguridad, al menos solo tengo que cambiar los detalles en un solo lugar:
fuente
Para que se muestre en sus páginas JSP, puede usar Spring Security Tag Lib:
http://static.springsource.org/spring-security/site/docs/3.0.x/reference/taglibs.html
Para usar cualquiera de las etiquetas, debe tener el taglib de seguridad declarado en su JSP:
Luego, en una página jsp, haga algo como esto:
NOTA: Como se menciona en los comentarios de @ SBerg413, deberá agregar
a la etiqueta "http" en la configuración de security.xml para que esto funcione.
fuente
Si está utilizando Spring Security ver> = 3.2, puede usar la
@AuthenticationPrincipal
anotación:Aquí,
CustomUser
hay un objeto personalizado que implementaUserDetails
que es devuelto por una costumbreUserDetailsService
.Puede encontrar más información en el capítulo @AuthenticationPrincipal de los documentos de referencia de Spring Security.
fuente
HttpServletRequest.getUserPrincipal () obtiene un usuario autenticado;
Ejemplo:
fuente
null
si el usuario se autentica de forma anónima (http
>anonymous
elementos en Spring Security XML).SecurityContextHolder
oSecurityContextHolderStrategy
es la forma correcta.En Spring 3+ tienes las siguientes opciones.
Opción 1 :
Opcion 2 :
Opcion 3:
Opción 4: Fantasía: mira esto para más detalles
fuente
@CurrentUser
que funciona como la costumbre@ActiveUser
de su enlace.@AuthenticationPrincipal
con una@CurrentUser
anotación personalizada . Desde 3.2 no necesitamos implementar un solucionador de argumentos personalizado como en la respuesta vinculada. Esta otra respuesta tiene más detalles.Sí, las estadísticas son generalmente malas, generalmente, pero en este caso, la estática es el código más seguro que puede escribir. Dado que el contexto de seguridad asocia un Principal con el subproceso que se está ejecutando actualmente, el código más seguro accedería a la estática del subproceso lo más directamente posible. Ocultar el acceso detrás de una clase de contenedor que se inyecta proporciona al atacante más puntos para atacar. No necesitarían acceso al código (que les costaría mucho cambiar si se firmara el jar), solo necesitan una forma de anular la configuración, que se puede hacer en tiempo de ejecución o deslizar algún XML en el classpath. Incluso el uso de la inyección de anotaciones en el código firmado sería reemplazable con XML externo. Tal XML podría inyectar al sistema en ejecución con un principal falso.
fuente
Yo solo haría esto:
fuente
SecurityContextHolderAwareRequestFilter
que envuelve la solicitud e implementa esta llamada accediendo aSecurityContextHolder
.Para la última aplicación Spring MVC que escribí, no inyecté el soporte de SecurityContext, pero tenía un controlador base que tenía dos métodos de utilidad relacionados con esto ... isAuthenticated () & getUsername (). Internamente hacen la llamada al método estático que describió.
Al menos, solo está en el lugar una vez si necesita refactorizar más tarde.
fuente
Podrías usar el enfoque Spring AOP. Por ejemplo, si tiene algún servicio, necesita saber el director actual. Puede introducir anotaciones personalizadas, es decir, @Principal, que indican que este Servicio debe depender del director.
Luego, en su consejo, que creo que necesita extender MethodBeforeAdvice, verifique que el servicio en particular tenga una anotación @Principal e inyecte el nombre Principal, o configúrelo en 'ANÓNIMO' en su lugar.
fuente
El único problema es que incluso después de autenticarse con Spring Security, el bean de usuario / principal no existe en el contenedor, por lo que la inyección de dependencias será difícil. Antes de usar Spring Security, creábamos un bean de ámbito de sesión que tenía el Principal actual, lo inyectaba en un "AuthService" y luego inyectaba ese Servicio en la mayoría de los otros servicios de la Aplicación. Entonces, esos Servicios simplemente llamarían a authService.getCurrentUser () para obtener el objeto. Si tiene un lugar en su código donde obtiene una referencia al mismo Principal en la sesión, simplemente puede establecerlo como una propiedad en su bean de ámbito de sesión.
fuente
Prueba esto
fuente
La mejor solución si está utilizando Spring 3 y necesita el principal autenticado en su controlador es hacer algo como esto:
fuente
Estoy usando la
@AuthenticationPrincipal
anotación en las@Controller
clases, así como en las@ControllerAdvicer
anotadas. Ex.:¿Dónde
UserActive
está la clase que uso para los servicios de usuarios registrados y se extiende desdeorg.springframework.security.core.userdetails.User
? Algo como:Realmente fácil.
fuente
Defínalo
Principal
como una dependencia en su método de controlador y spring inyectará al usuario autenticado actual en su método en la invocación.fuente
Me gusta compartir mi forma de apoyar los detalles del usuario en la página de Freemarker. ¡Todo es muy simple y funciona perfectamente!
Solo tiene que colocar la solicitud de autenticación en
default-target-url
(página después del inicio de sesión del formulario) Este es mi método Controler para esa página:Y este es mi código ftl:
Y eso es todo, el nombre de usuario aparecerá en cada página después de la autorización.
fuente