Integración de Spring Security en el framework RuoYi: Autenticación y Autorización Detalladas

El framework RuoYi ya incluye una integración profunda con Spring Security, no es una adición posterior. La lógica central combina las capacidades de Spring Security para autenticación, autorización e intercepción de recursos con el modelo RBAC (Usuario-Rol-Menú-Botón) de RuoYi, tokens JWT y reglas de autorización personalizadas para satisfacer las complejas necesidades de gestión de permisos de las aplicaciones de administración.

El flujo de interacción principal implica:

Pasos Clave de Integración (Implementación Nativa de RuoYi, Adaptable)

A continuación, se describen los pasos de configuración esenciales para la integración de Spring Security en RuoYi, que puedes usar como referencia o para personalizaciones:

Paso 1: Inclusión de Dependencias Centrales (Incluidas en RuoYi)

En el archivo pom.xml de ruoyi-admin, RuoYi ya ha incorporado las dependencias de Spring Security y JWT:

<!-- Dependencia central de Spring Security -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- Dependencia JWT (extensión personalizada de RuoYi) -->
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.1</version>
</dependency>

Paso 2: Configuración de la Clase Central de Spring Security

SecurityConfig.java en RuoYi es el núcleo de la integración. Es responsable de configurar el AuthenticationManager, las reglas de intercepción de recursos, los filtros personalizados, etc.

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    // Inyectar la lógica de autenticación de usuario personalizada de RuoYi
    @Autowired
    private UserDetailsService userDetailsService;

    // Inyectar el filtro de autenticación JWT
    @Autowired
    private JwtAuthenticationTokenFilter authenticationTokenFilter;

    // Inyectar la clase de manejo de falta de autorización personalizada
    @Autowired
    private AuthenticationEntryPointImpl unauthorizedHandler;

    // Inyectar la clase de manejo de éxito de cierre de sesión personalizado
    @Autowired
    private LogoutSuccessHandlerImpl logoutSuccessHandler;

    /**
     * Configura el AuthenticationManager.
     */
    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    /**
     * Configura el codificador de contraseñas.
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    /**
     * Configura la lógica de autenticación de usuario.
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
    }

    /**
     * Configura las reglas de intercepción de recursos.
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            // Deshabilitar CSRF y CORS
            .cors().and().csrf().disable()
            // Manejo de excepciones (falta de autorización)
            .exceptionHandling().authenticationEntryPoint(unauthorizedHandler).and()
            // Política de sesión sin estado (sin creación de sesión)
            .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()
            // Configuración de reglas de intercepción
            .authorizeRequests()
            // Permitir acceso a las interfaces de login y captura de imágenes, y recursos estáticos
            .antMatchers("/login", "/captchaImage").anonymous()
            // Permitir acceso a la documentación de Swagger
            .antMatchers("/swagger/**", "/v2/api-docs", "/doc.html").permitAll()
            // Todas las demás solicitudes requieren autenticación
            .anyRequest().authenticated()
            .and()
            // Configurar el cierre de sesión
            .logout().logoutUrl("/logout").logoutSuccessHandler(logoutSuccessHandler);

        // Agregar el filtro JWT (antes del UsernamePasswordAuthenticationFilter)
        http.addFilterBefore(authenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
    }
}

Paso 3: Implementación Personalizada de UserDetailsService (Núcleo de Autenticación de Usuario)

RuoYi implementa la interfaz UserDetailsService para cargar información del usuario desde la base de datos (nombre de usuario, contraseña, roles, permisos), que Spring Security utiliza para la autenticación.

@Service
public class UserDetailsServiceImpl implements UserDetailsService {

    @Autowired
    private SysUserMapper userMapper;
    @Autowired
    private SysRoleMapper roleMapper;
    @Autowired
    private SysMenuMapper menuMapper;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        // 1. Consultar información básica del usuario
        SysUser user = userMapper.selectUserByUserName(username);
        if (StringUtils.isNull(user)) {
            throw new UsernameNotFoundException("El usuario no existe");
        }
        // 2. Consultar roles del usuario
        List<SysRole> roles = roleMapper.selectRolePermissionByUserId(user.getUserId());
        // 3. Consultar permisos del usuario (menús + botones)
        List<String> permissions = menuMapper.selectMenuPermsByUserId(user.getUserId());
        // 4. Empaquetar como objeto UserDetails de Spring Security
        return new LoginUser(user, roles, permissions);
    }
}

Paso 4: Filtro JWT Personalizado (Verificación de Tokens)

RuoYi extiende la cadena de filtros de Spring Security para implementar la verificación de tokens JWT, reemplazando la autenticación tradicional basada en sesiones.

@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {

    @Autowired
    private TokenService tokenService;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
            throws ServletException, IOException {
        // 1. Obtener el token del encabezado de la solicitud (por defecto: Authorization, prefijo: Bearer)
        LoginUser loginUser = tokenService.getLoginUser(request);
        // 2. Verificar la validez del token y si la autenticación aún no se ha completado
        if (StringUtils.isNotNull(loginUser) && StringUtils.isNull(SecurityUtils.getAuthentication())) {
            // 3. Verificar la fecha de expiración del token
            tokenService.verifyToken(loginUser);
            // 4. Colocar la información del usuario en el contexto de Spring Security para completar la autenticación
            UsernamePasswordAuthenticationToken authenticationToken =
                new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities());
            authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
            SecurityContextHolder.getContext().setAuthentication(authenticationToken);
        }
        // Continuar la cadena de filtros
        chain.doFilter(request, response);
    }
}

Paso 5: Anotaciones de Permisos Personalizadas (Control de Permisos a Nivel de Interfaz/Botón)

RuoYi se basa en el control de permisos a nivel de método de Spring Security y extiende la anotación personalizada @PreAuthorize para lograr un control de permisos granular.

// Ejemplo 1: Control de permisos a nivel de interfaz (solo roles con permiso 'system:user:list' pueden acceder)
@GetMapping("/list")
@PreAuthorize("@ss.hasPermi('system:user:list')")
public TableDataInfo list(SysUser user) {
    // Lógica de negocio
}

// Ejemplo 2: Control de permisos a nivel de rol (solo el rol ADMIN puede acceder)
@GetMapping("/delete")
@PreAuthorize("hasRole('ADMIN')")
public AjaxResult delete(Long userId) {
    // Lógica de negocio
}

En resumen, RuoYi integra nativamente Spring Security. La autenticación de usuarios se maneja extendiendo UserDetailsService, la verificación de tokens JWT se implementa con filtros personalizados, y el control de permisos granular se logra a través de anotaciones como @PreAuthorize. La configuración central se encuentra en SecurityConfig.java, con puntos clave de extensión que incluyen la carga de información del usuario, filtros de validación de tokens, anotaciones de permisos y manejo de excepciones. Al adaptar el sistema a tus necesidades, puedes extender esta base de integración en lugar de reconstruir el sistema Spring Security desde cero.

Etiquetas: Spring Security RuoYi JWT rbac autenticación

Publicado el 6-9 03:23