Mise en place des AuthenticationProvider au Printemps de Sécurité 2.06
Je suis en utilisant le Printemps de Sécurité pour sécuriser un Struts2 application web. En raison des contraintes du projet, je suis à l'aide de Spring Security 2.06.
Mon équipe a construit un Utilisateur personnalisée de Gestion des API qui permet d'authentifier un utilisateur après la prise dans le nom d'utilisateur et le mot de passe des paramètres, et retourne un utilisateur personnalisée objet contenant une liste de rôles et d'autres attributs comme le courrier électronique, nom, etc.
À partir de ma compréhension, typique de la Sécurité Printemps de cas d'utilisation utilise par défaut UserDetailsService pour récupérer un UserDetails objet; cet objet contient (entre autres choses) un champ de mot de passe qui sera utilisé par le framework pour authentifier l'utilisateur.
Dans mon cas, je veux que notre API ne l'authentification, puis retour personnalisé UserDetails objet contenant les rôles et les autres attributs (e-mail, etc).
Après quelques recherches, j'ai compris que je peux le faire par le biais d'une implémentation personnalisée de AuthenticationProvider. J'ai aussi des implémentations personnalisées de UserDetailsService et UserDetails.
Mon problème est que je ne comprends pas vraiment ce que je suis censé être de retour dans CustomAuthenticationProvider. Puis-je utiliser mon custom UserDetailsService objet ici? C'est que même nécessaire? Désolé, je suis vraiment confus.
CustomAuthenticationProvider:
public class CustomAuthenticationProvider implements AuthenticationProvider {
private Logger logger = Logger.getLogger(CustomAuthenticationProvider.class);
private UserDetailsService userDetailsService; //what am i supposed to do with this?
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
UsernamePasswordAuthenticationToken auth = (UsernamePasswordAuthenticationToken) authentication;
String username = String.valueOf(auth.getPrincipal());
String password = String.valueOf(auth.getCredentials());
logger.info("username:" + username);
logger.info("password:" + password);
/* what should happen here? */
return null; //what do i return?
}
@Override
public boolean supports(Class aClass) {
return true; //To indicate that this authenticationprovider can handle the auth request. since there's currently only one way of logging in, always return true
}
public UserDetailsService getUserDetailsService() {
return userDetailsService;
}
public void setUserDetailsService(UserDetailsService userDetailsService) {
this.userDetailsService = userDetailsService;
}
}
applicationContext-security.xml:
<beans:bean id="customUserDetailsService" scope="prototype" class="com.test.testconsole.security.CustomUserDetailsService"/>
<beans:bean id="customAuthenticationProvider" class="com.test.testconsole.security.CustomAuthenticationProvider">
<custom-authentication-provider />
<beans:property name="userDetailsService" ref="customUserDetailsService" />
</beans:bean>
Pour résumer, c'est ce dont j'ai besoin:
- Utilisateur se connecte par le biais d'un formulaire web
- Authentifier l'utilisateur à l'aide de la maison de l'utilisateur de l'API de gestion
- De succès pour les utilisateurs authentifiés, remplir GrantedAuthories, etc.
-
Retour d'un utilisateur de l'entité contenant des rôles et des pouvoirs, et d'autres attributs comme le courrier électronique, nom, etc. Je dois être en mesure d'accéder à cet objet comme si ..
//spring security get user name Authentication auth = SecurityContextHolder.getContext().getAuthentication(); userName = auth.getName(); //get logged in username logger.info("username: " + userName); //spring security get user role GrantedAuthority[] authorities = auth.getAuthorities(); userRole = authorities[0].getAuthority(); logger.info("user role: " + userRole);
J'espère que cela a du sens. Toute aide ou des pointeurs qui sera apprécié!
Merci!
Mise à jour:
J'ai fait quelques progrès, je pense.
J'ai un objet d'Authentification personnalisé la mise en œuvre de l'Authentification de l'interface:
public class CustomAuthentication implements Authentication {
String name;
GrantedAuthority[] authorities;
Object credentials;
Object details;
Object principal;
boolean authenticated;
public CustomAuthentication(String name, GrantedAuthority[] authorities, Object credentials, Object details, Object principal, boolean
authenticated){
this.name=name;
this.authorities=authorities;
this.details=details;
this.principal=principal;
this.authenticated=authenticated;
}
@Override
public GrantedAuthority[] getAuthorities() {
return new GrantedAuthority[0]; //To change body of implemented methods use File | Settings | File Templates.
}
@Override
public Object getCredentials() {
return null; //To change body of implemented methods use File | Settings | File Templates.
}
@Override
public Object getDetails() {
return null; //To change body of implemented methods use File | Settings | File Templates.
}
@Override
public Object getPrincipal() {
return null; //To change body of implemented methods use File | Settings | File Templates.
}
@Override
public boolean isAuthenticated() {
return false; //To change body of implemented methods use File | Settings | File Templates.
}
@Override
public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
//To change body of implemented methods use File | Settings | File Templates.
}
@Override
public String getName() {
return null;
}
}
et mis à jour mon CustomerAuthenticationProvider classe:
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
UsernamePasswordAuthenticationToken auth = (UsernamePasswordAuthenticationToken) authentication;
String username = String.valueOf(auth.getPrincipal());
String password = String.valueOf(auth.getCredentials());
logger.info("username:" + username);
logger.info("password:" + password);
//no actual validation done at this time
GrantedAuthority[] authorities = new GrantedAuthorityImpl[1];
authorities[0] = new GrantedAuthorityImpl("ROLE_USER");
CustomAuthentication customAuthentication = new CustomAuthentication("TestMerchant",authorities,"details",username,password,true);
return customAuthentication;
//return new UsernamePasswordAuthenticationToken(username,password,authorities);
}
Il fonctionne si je retourne un UsernamePasswordAuthenticationToken objet, mais si j'essaie de revenir CustomAuthentication, j'obtiens l'erreur suivante:
java.lang.ClassCastException: com.test.testconsole.security.CustomAuthentication cannot be cast to org.springframework.security.providers.UsernamePasswordAuthenticationToken
at com.test.testconsole.security.CustomAuthenticationProvider.authenticate(CustomAuthenticationProvider.java:27)
at org.springframework.security.providers.ProviderManager.doAuthentication(ProviderManager.java:188)
at org.springframework.security.AbstractAuthenticationManager.authenticate(AbstractAuthenticationManager.java:46)
at org.springframework.security.intercept.AbstractSecurityInterceptor.authenticateIfRequired(AbstractSecurityInterceptor.java:319)
at org.springframework.security.intercept.AbstractSecurityInterceptor.beforeInvocation(AbstractSecurityInterceptor.java:258)
at org.springframework.security.intercept.web.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:106)
at org.springframework.security.intercept.web.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:83)
at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
at org.springframework.security.ui.SessionFixationProtectionFilter.doFilterHttp(SessionFixationProtectionFilter.java:67)
at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
at org.springframework.security.ui.ExceptionTranslationFilter.doFilterHttp(ExceptionTranslationFilter.java:101)
at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
at org.springframework.security.providers.anonymous.AnonymousProcessingFilter.doFilterHttp(AnonymousProcessingFilter.java:105)
at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
at org.springframework.security.ui.rememberme.RememberMeProcessingFilter.doFilterHttp(RememberMeProcessingFilter.java:116)
at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
at org.springframework.security.wrapper.SecurityContextHolderAwareRequestFilter.doFilterHttp(SecurityContextHolderAwareRequestFilter.java:91)
at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
at org.springframework.security.ui.basicauth.BasicProcessingFilter.doFilterHttp(BasicProcessingFilter.java:174)
at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
at org.springframework.security.ui.AbstractProcessingFilter.doFilterHttp(AbstractProcessingFilter.java:278)
at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
at org.springframework.security.ui.logout.LogoutFilter.doFilterHttp(LogoutFilter.java:89)
at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
at org.springframework.security.context.HttpSessionContextIntegrationFilter.doFilterHttp(HttpSessionContextIntegrationFilter.java:235)
at org.springframework.security.ui.SpringSecurityFilter.doFilter(SpringSecurityFilter.java:53)
at org.springframework.security.util.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:390)
at org.springframework.security.util.FilterChainProxy.doFilter(FilterChainProxy.java:175)
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:236)
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:167)
at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1157)
at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:388)
at org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216)
at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:182)
at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:765)
at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:418)
at org.mortbay.jetty.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:230)
at org.mortbay.jetty.handler.HandlerCollection.handle(HandlerCollection.java:114)
at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
at org.mortbay.jetty.Server.handle(Server.java:326)
at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:536)
at org.mortbay.jetty.HttpConnection$RequestHandler.headerComplete(HttpConnection.java:915)
at org.mortbay.jetty.HttpParser.parseNext(HttpParser.java:539)
at org.mortbay.jetty.HttpParser.parseAvailable(HttpParser.java:212)
at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:405)
at org.mortbay.io.nio.SelectChannelEndPoint.run(SelectChannelEndPoint.java:409)
at org.mortbay.thread.QueuedThreadPool$PoolThread.run(QueuedThreadPool.java:582)
C'est comme quelque chose qui ne s'attendant pas seulement un objet d'Authentification, mais une mise en œuvre spécifique de l'il - UsernamePasswordAuthenticationToken. Cela me fait penser que j'ai peut-être manquant un autre composant personnalisé .. peut-être un filtre?
Vous devez vous connecter pour publier un commentaire.
Si vous implémentez votre propre
AuthenticationProvider
, Vous n'avez pas à mettre en œuvre unUserDetailsService
si vous ne souhaitez pas.UserDetailsService
fournit simplement un standard de DAO pour le chargement des informations de l'utilisateur et de certaines autres catégories dans le cadre sont mises en œuvre pour l'utiliser.Normalement, pour authentifier à l'aide d'un nom d'utilisateur et le mot de passe, vous instanciez un
DaoAuthenticationProvider
et de les injecter avec unUserDetailsService
. Qui peut encore être votre meilleure approche. Si vous implémentez votre propre fournisseur, vous prenez la responsabilité de s'assurer que l'utilisateur a fourni le mot de passe correct et ainsi de suite. Cependant, dans certains cas, c'est une approche plus simple.Pour répondre à votre "ce qui doit se passer ici?" commentaire dans le code, ce serait quelque chose comme
L'utilisateur de l'objet sera alors accessible à l'aide de la
méthode, et vous pouvez accéder à d'autres propriétés (e-mail, etc) par moulage personnalisé de l'utilisateur mise en œuvre.
La façon dont vous chargez les données de l'utilisateur est à vous. Tous les Printemps de Sécurité se soucie ici est la
AuthenticationProvider
interface.Vous devriez également stocker des mots de passe hachés et de valider le mot de passe fourni à l'aide du même algorithme, plutôt que d'un simple contrôle d'égalité.
merci pour l'affichage de ce Luc!
M'a sauvé de plus des dommages au cerveau.
Seule chose à noter, j'ai couru dans, pour toute personne qui se soucie de:
Ma configuration:
Lors de l'utilisation de la grandement apprécié simplifié/élégant approche Luc suggère, ne PAS mettre en œuvre une coutume UserDetails (ou UserDetailsService) de l'objet -et - à l'aide de votre propre Utilisateur domaine objet qui ne s'étend pas quelque chose de spécial, vous doit prendre une étape supplémentaire, si vous êtes à l'aide de la "sec" balises personnalisées à partir du printemps de sécurité (dans vos pages de cours):
Lorsque vous instanciez une base, non personnalisé UsernamePasswordAuthenticationToken, vous DEVEZ passer une instanciation de quelque chose qui s'étend Principal, encore une fois, si vous voulez que votre printemps de sécurité personnalisé écart des balises de travail. J'ai fait quelque chose comme cela, pour le garder aussi simple que possible (le référencement de mon domaine de valeurs de l'objet là où c'est utile/y a lieu):
Ce qui devrait satisfaire les conditions testées dans le graal.les plugins.springsecurity.SecurityTagLib.determineSource() donc, vous savez, vos pages qui utilisent
<sec:loggedInUserInfo>
seront effectivement rendre:Sinon, si vous instanciez le UsernamePasswordAuthenticationToken avec votre nom d'Utilisateur de domaine objet (comme Luke spectacle dans son exemple), que la sécurité tag lib méthode (determineSource()) fera juste que c'est le niveau le mieux et le retour de la (méta) valeur de org.codehaus.groovy.graal.commons.DefaultGrailsDomainClass et vous recevrez un message d'erreur lorsque l'étiquette est à la recherche pour le nom d'utilisateur de membre de la variable indiquant:
Court de re-mise en œuvre/sous-classer le printemps-sécurité-core plugin taglibs dans mon graal projet, il n'y a tout simplement pas de voie à la fois utiliser les taglibs ET utiliser votre domaine personnalisé de l'Utilisateur de la classe à instancier le jeton d'être passé à partir de votre filtre à votre fournisseur.
Puis de nouveau, une ligne de code est un très petit prix à payer 🙂