lunes, 15 de octubre de 2012


Spring Secutirty

 
Primero se crean tablas que serán utilizados para el propósito del Spring Security. Uno para el registro de usuarios y otro para el registro de perfiles. El siguiente script servirá para agregar las tablas NSBUSR01 y AUTHORITIES en el MySql.

 

CREATE TABLE `NSBUSR01` (
  `USRCOD`    int(4) not null,
  `USRAPEPAT` varchar(15),
  `USRAPEMAT` varchar(15),
  `USRNOM`    varchar(20),
  `USRLOG`    varchar(10),
  `CLAVE` varchar(50),
  `ESTADO` int(11),
  PRIMARY KEY (`USRCOD`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

INSERT INTO `NSBUSR01` VALUES
(1, 'SILVA', 'DIOS', 'MARYURI FABIOLA', 'MSILVA', ' e10adc3949ba59abbe56e057f20f883e ', 1)
,(3, 'MARTINEZ', 'JHON', 'LORENA', 'LMARTINEZ ', e10adc3949ba59abbe56e057f20f883e ', 1);

CREATE TABLE `AUTHORITIES` (
            `AUTHORITHI` varchar(50) not null,
            `USERNAME` varchar(50) not null
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

INSERT INTO `AUTHORITIES` VALUES
('ROLE_USER', 'MSILVA')
,('ROLE_ADMIN', 'MSILVA')
,('ROLE_USER', 'LMARTINEZ');

 

En el pom.xml agregar las dependencias para que el Maven descargue las librerías necesarias para activar el Spring Security.

 

     <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-core</artifactId>
        <version>3.0.3.RELEASE</version>
     </dependency>
     <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-config</artifactId>
        <version>3.0.3.RELEASE</version>
    </dependency>
     <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-acl</artifactId>
        <version>3.0.3.RELEASE</version>
    </dependency>
     <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-taglibs</artifactId>
        <version>3.0.3.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-web</artifactId>
        <version>3.0.3.RELEASE</version>
    </dependency>

 

Luego agregar dentro de la carpeta WEB-INF un archivo con nombre security-config.xml, en donde se especifica el tag principal <http> en donde se indica que carpetas y direcciones no se aplicará la seguridad, como es el caso de la página del login.htm que siempre deberá mostrarse a todos los usuarios. Además dentro de este tag se inidica el tag <form-login> en donde se especifica las páginas por defecto (default-target-url), a donde redireccionará en caso de hacer loguin (login-page) o en caso surja algún error de autenticación (authentication-failure-url). Así mismo hay un tag para especificar la página que redireccionará en caso se haga logout (logout-success-url) en el tag <logout>. Finalmente, en otro tag principal < authentication-manager> especifica el alias del servicio que realizará la autenticación (user-service-ref) en al tag <authentication-provider> y también se puede especificar el tipo de encriptación de la contraseña (hash) en el tag <password-encoder>.

 

<?xml version="1.0" encoding="UTF-8"?>
<b:beans xmlns="http://www.springframework.org/schema/security"
         xmlns:b="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
           http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
           http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.0.xsd">

        <!-- Configure Spring Security -->
        <http auto-config="true">
                <intercept-url pattern="/css/*" filters="none"/>
                <intercept-url pattern="/img/*" filters="none"/>
                <intercept-url pattern="/js/*" filters="none"/>
                <intercept-url pattern="/images/*" filters="none"/>
                <intercept-url pattern="/login.htm" filters="none"/>
                <intercept-url pattern="/redirect.jsp" filters="none"/>
                <intercept-url pattern="/**" access="ROLE_USER"/>
                <form-login login-page="/login.htm"
                                        default-target-url="/home.htm"
                                        authentication-failure-url="/login.htm?login_error=1"
                                        always-use-default-target="true" />
                <http-basic/>               
                <logout invalidate-session="true" logout-success-url="/login.htm" />                               
                <remember-me />
        </http>
 
        <authentication-manager>
                <authentication-provider user-service-ref="userLoginService" >                       
                     <password-encoder hash="md5" />                       
                </authentication-provider>          
        </authentication-manager>
</b:beans>

 

Luego en el applicationContext.xml agregar la importación del archivo de configuración del spring security:

 

<import resource="security-config.xml" />

 

Se agrega el modelo de la clase objeto UsuariosSystem.java que refleja los atributos del usuario en la base de datos con sus getter y setters. Además contiene la implementación de la lista para obtener los perfiles de los usuarios, así como la implementación del constructor UsuariosSystem que servirá al spring security para realizar la consulta de la autorización de usuarios en el loguin.

 

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.GrantedAuthorityImpl;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.User;

public class UsuariosSystem extends User{

            private static final long serialVersionUID = 1L;
 
            private Integer usrcod;
            private String usrapepat;
            private String usrapemat;
            private String usrnom;
            private String usrlog;
            private String clave;
            private Integer estado;

             public static UsuariosSystem getUsuarioBean() {
                         UsuariosSystem nu = (UsuariosSystem)(SecurityContextHolder.getContext().getAuthentication().getPrincipal());
         if(nu != null) {
             return nu;
         }
         else return null;
             }

             public static List<GrantedAuthority> uno(){
         List<GrantedAuthority> oo = new ArrayList<GrantedAuthority>();
         oo.add(new GrantedAuthorityImpl("IS_AUTHENTICATED_ANONYMOUSLY") );
         return oo;
             }

             public UsuariosSystem(){
                        super("default", "default", true, true, true, true , uno() );
             }

            public UsuariosSystem(String username, String password, boolean enabled, Collection<GrantedAuthority> authorities) {
                          super(username, password, enabled, true, true, true, authorities);
          this.estado = enabled==true ? 1 : 0;
          this.usrlog = username;
          this.clave = password;
            }         

            public UsuariosSystem(String username, String password, boolean enabled,List<GrantedAuthority> authorities,
                                   Integer usrcod, String usrapepat, String usrapemat,
                                   String usrnom, String usrlog
) {
                        super(username, password, enabled, true, true, true, authorities);
                        this.estado = enabled==true ? 1 : 0;
        this.usrlog = username;
        this.clave = password;
                        this.usrcod = usrcod;
                        this.usrapepat = usrapepat;
                        this.usrapemat = usrapemat;
                        this.usrnom = usrnom;
                        this.usrlog = usrlog;
            }
 
            public Integer getUsrcod() {
                        return usrcod;
            }
            public void setUsrcod(Integer usrcod) {
                        this.usrcod = usrcod;
            }
            public String getUsrapepat() {
                        return usrapepat;
            }
            public void setUsrapepat(String usrapepat) {
                        this.usrapepat = usrapepat;
            }
            public String getUsrapemat() {
                        return usrapemat;
            }
            public void setUsrapemat(String usrapemat) {
                        this.usrapemat = usrapemat;
            }
            public String getUsrnom() {
                        return usrnom;
            }
            public void setUsrnom(String usrnom) {
                        this.usrnom = usrnom;
            }
            public String getUsrlog() {
                        return usrlog;
            }
            public void setUsrlog(String usrlog) {
                        this.usrlog = usrlog;
            }
            public String getClave() {
                        return clave;
            }
            public void setClave(String clave) {
                        this.clave = clave;
            }
            public Integer getEstado() {
                        return estado;
            }
            public void setEstado(Integer estado) {
                        this.estado = estado;
            }
}

 

En la clase UsuariosDAOImpl.java se implementa el método obtenerUsuarioPorUsername para obtener datos adicionales del usuario

 

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.springframework.stereotype.Repository;
import pe.com.munmiraflores.gestiondocumental.domain.UsuariosSystem;

@Repository
public class UsuariosDAOImpl  extends BaseDAO implements UsuarioDao{

            @Override
            public UsuariosSystem obtenerUsuarioPorUsername(String username) {
                        Connection con = null;
                        PreparedStatement stmt = null;
                        ResultSet rs = null;
                        UsuariosSystem usr = null;
                        try {
                                   String query = "Select * from nsbusr01 where usrlog = ? ";
                                   con = ConexionBD.obtenerConexion();
                                   stmt = con.prepareStatement(query);
                                   stmt.setString(1, username);
                                   rs = stmt.executeQuery();
                                   if (rs.next()) {
                                               usr = new UsuariosSystem(rs.getString("usrlog"), rs.getString("clave"), rs.getInt("estado")==1?true:false, UsuariosSystem.uno() );
                                               usr.setUsrapepat( rs.getString("usrapepat"));
                                   }
                        } catch (SQLException e) {
                                   System.err.println(e.getMessage());
                        } finally {
                                   this.cerrarResultSet(rs);
                                   this.cerrarStatement(stmt);
                                   this.cerrarConexion(con);
                        }
                        return usr;
            }
}

 

En la clase AutenticacionJdbcDaoImpl.java que extiende de una interface en las librerías Spring, el org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl el cual especifica la implementación del método loadUserByUsername que recibe como parámetro el nombre del usuario que será autenticado para loguear al sistema. Y a su vez, en el método loadUserByUsername, se deberá especificar la ejecución del método setUsersByUsernameQuery el cual recibe la consulta que realizará la validación del usuario y del método setAuthoritiesByUsernameQuery el cual reciben las consultas para obtener el perfil del usuario logueado. Además, se ha agregado la ejecución del método obtenerUsuarioPorUsername que viene de la clase UsuariosDAOImpl  para obtener datos adicionales como el nombre y apellidos del usuario logueado.

 

import javax.sql.DataSource;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl;
import org.springframework.stereotype.Repository;
import pe.com.munmiraflores.gestiondocumental.dao.UsuarioDao;
import pe.com.munmiraflores.gestiondocumental.domain.UsuariosSystem;

@Repository("userLoginService")
public class AutenticacionJdbcDaoImpl extends JdbcDaoImpl{

            private Logger logger = Logger.getLogger(AutenticacionJdbcDaoImpl.class);

            @Autowired
            public AutenticacionJdbcDaoImpl(DataSource dataSource){
                        setDataSource(dataSource);
            }

            @Autowired
            private UsuarioDao usuarioDao;

            @Override
            public UserDetails loadUserByUsername(String username) {
                        try {
                                   this.setUsersByUsernameQuery("SELECT u.usrlog as username,u.clave as password, u.estado as enabled  FROM nsbusr01 u WHERE u.usrlog = ?");
                                   this.setAuthoritiesByUsernameQuery("SELECT  username ,authority FROM authorities WHERE username = ?");
                                   UserDetails user = super.loadUserByUsername(username);
                                   logger.debug("usuario login "+user.toString());
                                   UsuariosSystem usuario =  usuarioDao.obtenerUsuarioPorUsername(username);
                                   UsuariosSystem ubean = new UsuariosSystem(
                                                           user.getUsername(),
                                                           user.getPassword(),
                                                           user.isEnabled(),
                                                           user.getAuthorities() );
                                   ubean.setUsrlog(usuario.getUsrlog());
                                   ubean.setClave(user.getPassword());
                                   ubean.setEstado( user.isEnabled()==true?1:0);
                                   ubean.setUsrapemat( usuario.getUsrapemat());
                                   ubean.setUsrapepat( usuario.getUsrapepat());
                                   logger.debug("*********"+ubean.toString());
                                   return ubean;  
                        } catch (Exception e) {
                                   e.printStackTrace();
                                   throw new UsernameNotFoundException("No hay notaria relacionada a este usuario");
                        }
            }         
}

 

Se agregan en el archivo WEB-INF\web.xml el filtro y el mapeo del filtro del Spring Security.

 

<filter>
    <filter-name>springSecurityFilterChain</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
  </filter>
   <filter-mapping>
    <filter-name>springSecurityFilterChain</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>

 

Al momento de declarar el form en el JSP se llama en el “action” al método que aplicará la seguridad del spring. j_spring_security_check es el nombre estándar que llama al servicio que realizará el loguin.

 

<form name="f" action="${pageContext.request.contextPath}/j_spring_security_check" method="post">

 

Finalmente, con el siguiente código HTML y el tag c:if de las librerías del JSTL insertado en un JSP se permite mostrar mensajes de las excepciones en caso no se haya logrado hacer loguin.

 

<c:if test="${not empty param.login_error}">
                        <div class="errors" style="color: #000;" align="right">
                                   No pudo ingresar al sistema, Intentelo nuevamente.<br />
                                   Razon: ${sessionScope.SPRING_SECURITY_LAST_EXCEPTION.message}
                        </div>
            </c:if>

1 comentario:

  1. Excelente estaba buscando como mejorar la presentacion de datos del user logeado y tu post fue la solucion perfecta despues de leer cosas que complicaban bastante la situaicon, gracias (y)

    ResponderEliminar