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>
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