Sa-Token es un framework de seguridad ligero para Java que simplifica la gestión de autenticación, sesiones y control de acceso. A continuación, se detalla el proceso para integrar este ecosistema en una aplicación basada en Spring Boot, utilizando Redis como almacenamiento persistente para los tokens.
1. Configuración de dependencias
Para comenzar, es necesario añadir las librerías principales en el archivo pom.xml. En este caso, incluiremos el iniciador de Spring Boot, el conector para Redis y el soporte de AOP para el manejo de anotaciones.
<!-- Framework principal de Sa-Token -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-boot-starter</artifactId>
<version>1.34.0</version>
</dependency>
<!-- Persistencia de sesiones en Redis -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-dao-redis-jackson</artifactId>
<version>1.34.0</version>
</dependency>
<!-- Soporte para interceptores de anotaciones -->
<dependency>
<groupId>cn.dev33</groupId>
<artifactId>sa-token-spring-aop</artifactId>
<version>1.34.0</version>
</dependency>
2. Definición de parámetros en application.yml
La configuración global permite definir el comportamiento de los tokens, como su tiempo de vida y si se permite el inicio de sesión múltiple para una misma cuenta.
sa-token:
token-name: access_token
# Tiempo de expiración (segundos). -1 para infinito.
timeout: 7200
# Tiempo de inactividad permitido antes de cerrar sesión
activity-timeout: 3600
# Permitir múltiples sesiones concurrentes por usuario
is-concurrent: true
# Compartir el mismo token entre dispositivos del mismo usuario
is-share: true
# Formato del token generado
token-style: random-64
3. Control de acceso mediante anotaciones
Una de las ventajas competitivas de Sa-Token es el uso de anotaciones declarativas para proteger los puntos de acceso (endpoints). Por ejemplo, podemos restringir una operación de escritura solo a usuarios con permisos específicos.
@RestController
@RequestMapping("/api/v1/records")
public class RecordController {
@PostMapping("/add")
@SaCheckPermission(value = {"admin:write", "record:create"}, mode = SaMode.OR)
public ResponseData createRecord(@RequestBody RecordDTO dto) {
// Lógica de negocio para insertar registro
return ResponseData.ok();
}
}
4. Obtención dinámica de permisos y roles
Para que las anotaciones anteriores funcionen, el framework necesita saber qué permisos tiene asignados el usuario actual. Esto se logra implementando la interfaz StpInterface. Primero, definimos la consulta SQL para recuperar los privilegios desde la base de datos (considerando una estructura donde los roles y permisos se manejan mediante tipos JSON o tablas relacionales).
<select id="findPermissionsByUserId" resultType="String">
SELECT DISTINCT p.code
FROM sys_user u
JOIN sys_role r ON JSON_CONTAINS(u.role_ids, CAST(r.id AS CHAR))
JOIN sys_permission p ON JSON_CONTAINS(r.permission_ids, CAST(p.id AS CHAR))
WHERE u.id = #{id} AND u.is_active = 1
</select>
Luego, creamos la clase de configuración que conecta Sa-Token con nuestro proveedor de datos:
@Component
public class SecurityConfiguration implements StpInterface {
private final UserMapper userMapper;
public SecurityConfiguration(UserMapper userMapper) {
this.userMapper = userMapper;
}
/**
* Recupera la lista de permisos del usuario autenticado
*/
@Override
public List<String> getPermissionList(Object loginId, String loginType) {
Long userId = Long.parseLong(loginId.toString());
return new ArrayList<>(userMapper.findPermissionsByUserId(userId));
}
/**
* Recupera la lista de roles del usuario autenticado
*/
@Override
public List<String> getRoleList(Object loginId, String loginType) {
// Retornar lista vacía si la lógica se basa exclusivamente en permisos
return new ArrayList<>();
}
}
Con esta implementación, cada vez que se detecta una anotación de seguridad, Sa-Token invocará estos métodos para validar si el identificador del usuario (almacenado en la sesión) cuenta con las credenciales necesarias para proceder.