Eu tenho um aplicativo da Web Spring MVC que usa o Spring Security. Quero saber o nome de usuário do usuário conectado no momento. Estou usando o snippet de código fornecido abaixo. Essa é a maneira aceita?
Não gosto de chamar um método estático dentro deste controlador - que derrota todo o propósito do Spring, IMHO. Existe uma maneira de configurar o aplicativo para injetar o SecurityContext atual ou a autenticação atual?
@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
fonte
fonte
Respostas:
Se você estiver usando o Spring 3 , a maneira mais fácil é:
fonte
Muita coisa mudou no mundo da primavera desde que essa pergunta foi respondida. O Spring simplificou a obtenção do usuário atual em um controlador. Para outros beans, o Spring adotou as sugestões do autor e simplificou a injeção do 'SecurityContextHolder'. Mais detalhes estão nos comentários.
Essa é a solução que eu acabei usando. Em vez de usar
SecurityContextHolder
no meu controlador, quero injetar algo que seja usadoSecurityContextHolder
sob o capô, mas que abstraia a classe do tipo código do meu código. Não encontrei outra maneira de fazer isso além de rolar minha própria interface, assim:Agora, meu controlador (ou qualquer outro POJO) ficaria assim:
E, devido à interface ser um ponto de dissociação, o teste de unidade é simples. Neste exemplo eu uso o Mockito:
A implementação padrão da interface é assim:
E, finalmente, a configuração do Spring de produção é assim:
Parece um pouco tolo que o Spring, um recipiente de injeção de dependência de todas as coisas, não tenha fornecido uma maneira de injetar algo semelhante. Eu entendo que
SecurityContextHolder
foi herdado do acegi, mas ainda assim. O problema é que eles estão muito próximos - se apenasSecurityContextHolder
houvesse um getter para obter aSecurityContextHolderStrategy
instância subjacente (que é uma interface), você poderia injetar isso. Na verdade, eu até abri um problema com o Jira nesse sentido.Uma última coisa - eu mudei substancialmente a resposta que tinha aqui antes. Verifique o histórico se você estiver curioso, mas, como um colega de trabalho apontou para mim, minha resposta anterior não funcionaria em um ambiente multithread. O subjacente
SecurityContextHolderStrategy
usado porSecurityContextHolder
é, por padrão, uma instância deThreadLocalSecurityContextHolderStrategy
, que armazenaSecurityContext
s em aThreadLocal
. Portanto, não é necessariamente uma boa idéia injetar oSecurityContext
diretamente em um bean no momento da inicialização - pode ser necessário recuperá-lo deThreadLocal
cada vez, em um ambiente multithread, para que o correto seja recuperado.fonte
Eu concordo que ter que consultar o SecurityContext para o usuário atual fede, parece uma maneira muito improvável de lidar com esse problema.
Eu escrevi uma classe estática "auxiliar" para lidar com esse problema; é sujo, pois é um método global e estático, mas imaginei que, se mudarmos algo relacionado à segurança, pelo menos só preciso alterar os detalhes em um só lugar:
fonte
Para que ele apareça nas suas páginas JSP, você pode usar o Spring Security Tag Lib:
http://static.springsource.org/spring-security/site/docs/3.0.x/reference/taglibs.html
Para usar qualquer uma das tags, você deve ter o taglib de segurança declarado em seu JSP:
Em uma página jsp, faça algo assim:
NOTA: Conforme mencionado nos comentários de @ SBerg413, você precisará adicionar
para a tag "http" na configuração security.xml para que isso funcione.
fonte
Se você estiver usando o Spring Security ver> = 3.2, poderá usar a
@AuthenticationPrincipal
anotação:Aqui
CustomUser
está um objeto customizado que implementaUserDetails
que é retornado por um customizadoUserDetailsService
.Mais informações podem ser encontradas no capítulo @AuthenticationPrincipal dos documentos de referência do Spring Security.
fonte
Eu obtenho usuário autenticado por HttpServletRequest.getUserPrincipal ();
Exemplo:
fonte
null
se o usuário for autenticado anonimamente (http
>anonymous
elementos no XML do Spring Security).SecurityContextHolder
ouSecurityContextHolderStrategy
é o caminho correto.Na primavera 3+, você tem as seguintes opções.
Opção 1 :
Opção 2 :
Opção 3:
Opção 4: Fantasia: verifique isso para obter mais detalhes
fonte
@CurrentUser
que funciona como o costume@ActiveUser
do seu link.@AuthenticationPrincipal
com uma@CurrentUser
anotação personalizada . Desde o 3.2, não precisamos implementar um resolvedor de argumentos personalizado, como na resposta vinculada. Essa outra resposta tem mais detalhes.Sim, as estatísticas geralmente são ruins - geralmente, mas nesse caso, a estática é o código mais seguro que você pode escrever. Como o contexto de segurança associa um Principal ao encadeamento atualmente em execução, o código mais seguro acessa a estática do encadeamento o mais diretamente possível. Ocultar o acesso atrás de uma classe de invólucro injetada fornece ao invasor mais pontos para atacar. Eles não precisariam acessar o código (que dificilmente mudariam se o jar fosse assinado), eles apenas precisariam de uma maneira de substituir a configuração, o que pode ser feito em tempo de execução ou colocar um pouco de XML no caminho de classe. Mesmo usando a injeção de anotação no código assinado seria substituível pelo XML externo. Esse XML pode injetar no sistema em execução um principal não autorizado.
fonte
Eu apenas faria isso:
fonte
SecurityContextHolderAwareRequestFilter
que agrupa a solicitação e implementa essa chamada acessando oSecurityContextHolder
.Para o último aplicativo Spring MVC que escrevi, não injetei o titular SecurityContext, mas eu tinha um controlador básico que tinha dois métodos utilitários relacionados a isso ... isAuthenticated () & getUsername (). Internamente, eles fazem a chamada de método estático que você descreveu.
Pelo menos, é apenas um lugar, se você precisar refatorar mais tarde.
fonte
Você pode usar o Spring AOP. Por exemplo, se você tiver algum serviço, isso precisa conhecer o principal atual. Você pode introduzir anotações personalizadas, como @Principal, que indicam que este Serviço deve depender do principal.
Em seu conselho, que eu acho que precisa estender o MethodBeforeAdvice, verifique se o serviço específico tem anotação @Principal e injeta o nome Principal ou defina-o como 'ANÔNIMO'.
fonte
O único problema é que, mesmo após a autenticação com o Spring Security, o usuário / bean principal não existe no contêiner, portanto, a injeção de dependência será difícil. Antes de usarmos o Spring Security, criaríamos um bean com escopo de sessão com o Principal atual, injetar isso em um "AuthService" e injetar esse Serviço na maioria dos outros serviços no Aplicativo. Portanto, esses serviços simplesmente chamariam authService.getCurrentUser () para obter o objeto. Se você tiver um lugar no seu código onde obtém uma referência ao mesmo Principal na sessão, basta configurá-lo como uma propriedade no seu bean com escopo de sessão.
fonte
Tente isto
fonte
A melhor solução se você estiver usando o Spring 3 e precisar do principal autenticado em seu controlador é fazer algo assim:
fonte
Estou usando a
@AuthenticationPrincipal
anotação nas@Controller
classes e também nas@ControllerAdvicer
anotadas. Ex.:Onde
UserActive
é a classe que eu uso para serviços de usuários registrados e se estende deorg.springframework.security.core.userdetails.User
. Algo como:Muito fácil.
fonte
Defina
Principal
como uma dependência no seu método do controlador e o spring injetará o usuário autenticado atual no seu método na chamada.fonte
Eu gosto de compartilhar minha maneira de dar suporte aos detalhes do usuário na página do freemarker. Tudo é muito simples e funciona perfeitamente!
Você só precisa colocar a solicitação de autenticação novamente
default-target-url
(página após o login do formulário) Este é o meu método Controler para essa página:E este é o meu código ftl:
E é isso, o nome de usuário aparecerá em todas as páginas após a autorização.
fonte