intellij idea gui formularios form español con application java spring-security spring-security-ldap

java - gui - intellij idea show form



Múltiples mecanismos de autenticación en una sola aplicación usando Java Config (4)

Actualmente tengo un único mecanismo de autenticación en mi aplicación que es usar LDAP para autenticación y autorización. Mi configuración de seguridad se ve así

@Configuration @EnableWebMvcSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http .csrf().disable() .authorizeRequests() .anyRequest().fullyAuthenticated() .and() .httpBasic(); } @Configuration protected static class AuthenticationConfiguration extends GlobalAuthenticationConfigurerAdapter { @Value("${ldap-${env}.manager.dn}") private String managerDn; @Value("${ldap-${env}.manager.pass}") private String managerPass; @Value("${ldap-${env}.server.url}") private String url; @Value("${ldap.password.attribute:userPassword}") private String passwordAttr; @Override public void init(AuthenticationManagerBuilder auth) throws Exception { auth.ldapAuthentication().userDnPatterns("uid={0},ou=people").groupSearchBase("ou=groups") .groupSearchFilter("(member={0})").userSearchBase("ou=people").userSearchFilter("(uid={0})") .userDetailsContextMapper(new CustomLdapPersonContextMapper()) // .passwordCompare() // .passwordAttribute(passwordAttr) // .passwordEncoder(new PlaintextPasswordEncoder()) // .and() .contextSource().managerDn(managerDn).managerPassword(managerPass).url(url); } } }

Sin embargo, hay situaciones en las que los usuarios pueden ingresar con un token de sesión que puede autenticarse desde un servidor de claves de sesión y un token válido devuelve un nombre de usuario que luego puede usarse para cargar información de autorización de LDAP para ese usuario. Entonces, mi segundo mecanismo de autenticación debería ocurrir primero, si un token de sesión está presente en los encabezados http, debe realizar la autenticación del token y luego la búsqueda de ldap, y si no hay un token de sesión, debe caer al mecanismo de autenticación actual. ¿Cómo puedo agregar esta segunda capa de autenticación?


La respuesta aceptada tiene el problema de que la solicitud actual no se otorga, es decir. ¡solo para las siguientes solicitudes se establece la sesión! Por lo tanto, necesitaba configurar en el punto 2

public class MyAuthorizationFilter extends AbstractAuthenticationProcessingFilter { public MyAuthorizationFilter() { super( "/*" ); // allow any request to contain an authorization header } public Authentication attemptAuthentication( HttpServletRequest request, HttpServletResponse response ) throws AuthenticationException { if ( request.getHeader( "Authorization" ) == null ) { return null; // no header found, continue on to other security filters } // required to use the token myNewToken = new MyAuthorizationToken( request.getHeader( "Authorization" ) ); // and set in the current context ==> the current request is as well authorized SecurityContextHolder.getContext().setAuthentication(myNewToken); // return a new authentication token to be processed by the authentication provider return myNewToken; } @Override public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { // try to authenticate the current request attemptAuthentication((HttpServletRequest) req, (HttpServletResponse) res); super.doFilter(req, res, chain); } }

de lo contrario, la solicitud actual aún no se autentica, ¡aunque ya se ha creado una sesión! (Y los proveedores que no necesito, es decir, agregar filtro es suficiente).


Otra opción para agregar un segundo proveedor de autenticación: simplemente especifique otro en AuthenticationManagerBuilder . Debido a que la anotación @EnableWebSecurity está anotada con EnableGlobalAuthentication , puede configurar la instancia global de AuthenticationManagerBuilder . (Vea los javadocs para más detalles).

Por ejemplo, aquí tenemos un proveedor de autenticación LDAP, así como un proveedor de autenticación en memoria (codificado) (esto es algo que hacemos en el desarrollo para que los usuarios locales prueben):

@Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Value("${user.role}") private String userRole; // i.e. ROLE_APP_USER @Value("${include.test.users}") private boolean includeTestUsers; @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .antMatchers("/**/js/**").permitAll() .antMatchers("/**/images/**").permitAll() .antMatchers("/**/favicon.ico").permitAll() .antMatchers("/**/css/**").permitAll() .antMatchers("/**/fonts/**").permitAll() .antMatchers("/**").hasAnyRole(userRole) .and().formLogin().loginPage("/login").permitAll().and().logout().permitAll(); http.logout().logoutRequestMatcher(new AntPathRequestMatcher("/logout")); } @Autowired public void configureGlobal(AuthenticationManagerBuilder auth, LdapContextSource contextSource) throws Exception { auth.ldapAuthentication() .userSearchBase("OU=Users OU") .userSearchFilter("sAMAccountName={0}") .groupSearchBase("OU=Groups OU") .groupSearchFilter("member={0}") .contextSource(contextSource); if (includeTestUsers) { auth.inMemoryAuthentication().withUser("user").password("u").authorities(userRole); } } }


Pasé bastante tiempo dando vueltas a la seguridad de primavera cuando utilicé la configuración pura de Java. Hay algunos pasos involucrados para que esto funcione. Debería ser algo en este sentido. El proceso básico es el siguiente:

  • Cree filtros personalizados para verificar las solicitudes de información de autorización específica

  • Cada filtro devuelve un valor nulo (si no se encuentra ninguna autorización de ese tipo) o un AbstractAuthenticationToken personalizado

  • Si un filtro devuelve un token, cada método de soporte (clase) de AuthenticationProvider se invocará con ese token que devuelve verdadero | falso si intenta probar la autenticación

  • intentAuthentication se llamará en el AuthenticationProvider que admite el token. Aquí realiza cualquier llamada de servicio para autenticar al usuario. A continuación, puede lanzar LoginException o llamar a audit.setAuthenticated (true) y devolver el token para una autenticación exitosa.

He estado usando esta configuración durante un tiempo que admite varios métodos de autenticación (solicitud firmada, nombre de usuario / contraseña, oauth, etc.) y funciona bastante bien.

También puede pasar AuthenticationSuccessHandler''s y AuthenticationFailuersHandler''s a los filtros de seguridad personalizados para proporcionar estrategias de redireccionamiento personalizadas y manejo de fallas.

También asegúrese de configurar los emparejadores de hormigas en los constructores del filtro para controlar qué patrones de URL también aplican los filtros. Por ejemplo, un filtro de solicitud ldap probablemente debería verificarse con cualquier solicitud "/ *", mientras que un filtro de nombre de usuario / contraseña solo se puede verificar en POST para / iniciar sesión o algo similar.

Código de ejemplo:

1) Cree los AuthenticationToken personalizados para cada tipo de autenticación que desee admitir

public class LDAPAuthorizationToken extends AbstractAuthenticationToken { private String token; public LDAPAuthorizationToken( String token ) { super( null ); this.token = token; } public Object getCredentials() { return token; } public Object getPrincipal() { return null; } } public class OTPAuthorizationToken extends UsernamePasswordAuthenticationToken { private String otp; public OTPAuthorizationToken( String username, String password, String otp ) { super( username, password ); this.otp = otp; } public String getOTP() { return otp; } }

2) Crear filtros de seguridad personalizados para cada tipo

public class LDAPAuthorizationFilter extends AbstractAuthenticationProcessingFilter { @Autowired private UserDetailsService userDetailsService; public LDAPAuthorizationFilter() { super( "/*" ); // allow any request to contain an authorization header } public Authentication attemptAuthentication( HttpServletRequest request, HttpServletResponse response ) throws AuthenticationException { if ( request.getHeader( "Authorization" ) == null ) { return null; // no header found, continue on to other security filters } // return a new authentication token to be processed by the authentication provider return new LDAPAuthorizationToken( request.getHeader( "Authorization" ) ); } } public class OTPAuthorizationFilter extends AbstractAuthenticationProcessingFilter { @Autowired private UserDetailsService userDetailsService; public OTPAuthorizationFilter() { super( "/otp_login" ); } public Authentication attemptAuthentication( HttpServletRequest request, HttpServletResponse response ) throws AuthenticationException { if ( request.getParameter( "username" ) == null || request.getParameter( "password" ) == null || request.getParameter( "otp" ) == null ) { return null; } // return a new authentication token to be processed by the authentication provider return new OTPAuthorizationToken( request.getParameter( "username" ), request.getParameter( "password" ), request.getParameter( "otp" ) ); } }

3) Crear proveedores de autenticación personalizados

public class LDAPAuthenticationProvider implements AuthenticationProvider { @Autowired private MyAuthenticationService sampleService; @Override public Authentication authenticate( Authentication authentication ) throws AuthenticationException { LDAPAuthorizationToken auth = (LDAPAuthorizationToken)authentication; String username = sampleService.verifyToken( auth.getCredentials() ); if ( username == null ) { throw new LoginException( "Invalid Token" ); } auth.setAuthenticated( true ); return auth; } @Override public boolean supports( Class<?> authentication ) { if ( authentication.isAssignableFrom( LDAPAuthorizationToken.class ) ) { return true; } return false; } } public class OTPAuthenticationProvider implements AuthenticationProvider { @Autowired private MyAuthenticationService sampleService; @Override public Authentication authenticate( Authentication authentication ) throws AuthenticationException { OTPAuthorizationToken auth = (OTPAuthorizationToken)authentication; String error = sampleService.loginWithOTP( auth.getPrincipal(), auth.getCredentials(), auth.getOTP() ); if ( error != null ) { throw new LoginException( error ); } auth.setAuthenticated( true ); return auth; } @Override public boolean supports( Class<?> authentication ) { if ( authentication.isAssignableFrom( OTPAuthorizationToken.class ) ) { return true; } return false; } }

4) Configurar la seguridad de primavera

public class SecurityConfiguration extends WebSecurityConfigurerAdapter { @Override protected void configure( HttpSecurity http ) throws Exception { // configure filters http.addFilterBefore( new LDAPAuthorizationFilter(), UsernamePasswordAuthenticationFilter.class ); http.addFilterBefore( new OTPAuthorizationFilter(), UsernamePasswordAuthenticationFilter.class ); // configure authentication providers http.authenticationProvider( new LDAPAuthenticationProvider() ); http.authenticationProvider( new OTPAuthenticationProvider() ); // disable csrf http.csrf().disable(); // setup security http.authorizeRequests() .anyRequest() .fullyAuthenticated() .and().httpBasic(); } }

¡Espero que ayude!


Solo quiero agregar a la respuesta de mclema. Es posible que deba agregar la anulación para una autenticación exitosa y continuar la cadena de filtros o de lo contrario el usuario será redirigido a la URL predeterminada ("/") en lugar de la original (por ejemplo: / myrest / server / somemethod)

@Override protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException { SecurityContext context = SecurityContextHolder.createEmptyContext(); context.setAuthentication(authResult); SecurityContextHolder.setContext(context); chain.doFilter(request, response); }