Ruoyi-Radius详解
一、模块功能架构
核心目标:实现基于 RADIUS 协议的 AAA(认证、授权、计费)服务,与若依管理系统无缝集成,复用其用户体系与权限模型。
功能细分:
- 认证(Authentication):校验用户凭证(用户名/密码)合法性。
- 授权(Authorization):动态返回用户网络权限属性(如VLAN、IP池、带宽策略)。
- 计费(Accounting):记录用户会话时长、流量消耗等计费信息。
gitee地址:https://gitee.com/fanshown/ruoyi-radius
访问地址:http://localhost:8090/index
portal认证地址:http://localhost:8090/portal/wlan/index
二、项目结构与核心代码实现
1. 依赖配置 (pom.xml
)
<!-- 核心依赖 -->
<dependency>
<groupId>org.tinyradius</groupId>
<artifactId>jradius-core</artifactId>
<version>1.1.8</version> <!-- RADIUS协议解析库 -->
</dependency>
<dependency>
<groupId>com.ruoyi</groupId>
<artifactId>ruoyi-common</artifactId> <!-- 若依基础模块 -->
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId> <!-- 安全认证 -->
</dependency>
2. 配置文件 (application.yml
)
radius:
server:
auth-port: 1812 # RADIUS认证端口(默认1812)
acct-port: 1813 # RADIUS计费端口(默认1813)
secret: ${RADIUS_SECRET:ruoyi123} # 共享密钥(需与NAS设备配置一致)
datasource:
nas-table: sys_nas # 存储NAS设备信息的数据库表
user-radius-table: sys_user_radius # 用户扩展属性表(可选)
3. 核心类与实现原理
(1) RadiusServerConfig
(配置类)
@Configuration
public class RadiusServerConfig {
@Value("${radius.server.auth-port}")
private int authPort; // 从配置注入认证端口
@Value("${radius.server.secret}")
private String secret; // 共享密钥
@Bean
public RadiusServer radiusServer() {
return new RadiusServer(authPort, secret); // 初始化RADIUS服务实例
}
}
功能:初始化 RADIUS 服务器实例,绑定端口与共享密钥。
关键变量:
authPort
:监听认证请求的端口(1812)。secret
:加密通信的共享密钥,需与 NAS 设备配置一致。
(2) RuoyiRadiusHandler
(请求处理器)
public class RuoyiRadiusHandler implements PacketHandler {
@Autowired
private AuthenticationManager authenticationManager;
@Override
public RadiusPacket handlePacket(RadiusPacket request) {
// 1. 提取用户名密码
String username = request.getAttribute("User-Name").getValueString();
String password = request.getAttribute("User-Password").getValueString();
// 2. 调用Spring Security认证
UsernamePasswordAuthenticationToken token =
new UsernamePasswordAuthenticationToken(username, password);
Authentication auth = authenticationManager.authenticate(token);
// 3. 构建响应包
RadiusPacket response = new RadiusPacket(RadiusPacket.ACCESS_ACCEPT);
response.addAttribute("Reply-Message", "Welcome!");
// 4. 添加动态授权属性(如VLAN)
CustomAttributeFactory.addAttributes(response, auth.getAuthorities());
return response;
}
}
功能:处理 Access-Request
认证请求,集成 Spring Security 完成用户校验,并返回授权属性。
关键方法:
handlePacket()
:核心入口,解析请求并调用认证逻辑。addAttributes()
:根据用户角色动态添加 RADIUS 响应属性。
(3) RadiusUserDetailsService
(用户服务)
@Service
public class RadiusUserDetailsService implements UserDetailsService {
@Autowired
private SysUserMapper userMapper;
@Override
public UserDetails loadUserByUsername(String username) {
SysUser user = userMapper.selectUserByUserName(username);
if (user == null) throw new UsernameNotFoundException("用户不存在");
// 构建Spring Security用户对象
return new org.springframework.security.core.userdetails.User(
user.getUserName(),
user.getPassword(),
AuthorityUtils.createAuthorityList("ROLE_" + user.getRoles())
);
}
}
功能:从若依用户表 sys_user
加载用户信息,转换为 Spring Security 可识别的 UserDetails
对象。
关键变量:
SysUserMapper
:若依原生的用户数据访问接口。
(4) NasServiceImpl
(NAS设备服务)
@Service
public class NasServiceImpl implements NasService {
@Autowired
private NasMapper nasMapper;
@Override
public NasDevice validateNas(String ip, String secret) {
NasDevice nas = nasMapper.selectByIpAndSecret(ip, secret);
if (nas == null || nas.getStatus() != 0) {
throw new RadiusException("NAS设备未注册或已禁用");
}
return nas;
}
}
功能:校验请求来源的 NAS 设备是否合法(IP 和密钥是否匹配数据库记录)。
关键SQL:selectByIpAndSecret
查询 sys_nas
表验证设备。
三、测试验证全流程
1. 环境准备
- 启动若依后台:
cd ruoyi-admin && mvn spring-boot:run # 确保用户数据已初始化
- 启动Ruoyi-Radius服务:
cd ruoyi-radius && mvn spring-boot:run # 检查1812/1813端口监听
- 安装测试工具:
# 安装FreeRADIUS客户端(Linux) sudo apt-get install freeradius-utils
2. 添加测试数据
- 若依后台添加用户:
- 路径:
系统管理 -> 用户管理
,创建用户testuser
,状态设为“正常”。
- 路径:
- 数据库插入NAS设备:
INSERT INTO sys_nas (nas_name, ip_address, secret, status) VALUES ('Test-Device', '127.0.0.1', 'ruoyi123', 0);
3. 认证测试(Authentication)
radtest -t pap testuser password123 127.0.0.1 1812 0 ruoyi123
请求参数:
-t pap
:使用 PAP 认证协议。0
:NAS 端口号(通常为0)。ruoyi123
:与sys_nas.secret
一致的共享密钥。
成功响应:
Received Access-Accept ID 15
Reply-Message = "Authentication Success"
Tunnel-Type:VLAN = 13
Tunnel-Medium-Type:IEEE-802 = 6
Tunnel-Private-Group-Id:VLAN = "100"
日志验证:
DEBUG c.r.r.handler.RuoyiRadiusHandler - 用户 testuser 认证通过
DEBUG c.r.r.attr.CustomAttributeFactory - 分配VLAN 100 至用户 testuser
4. 授权测试(Authorization)
验证动态属性返回:
- 检查响应包中的
Tunnel-Private-Group-Id
是否与用户角色关联的 VLAN 一致。 - 若需自定义属性(如带宽),需在
sys_user_radius
表添加字段,并在CustomAttributeFactory
中实现逻辑。
5. 计费测试(Accounting)
# 模拟计费请求(需实现AccountingHandler)
raclient -f /path/to/acct-request.txt 127.0.0.1 1813 ruoyi123
请求文件示例 (acct-request.txt
):
Acct-Status-Type = Start
User-Name = "testuser"
Acct-Session-Id = "123456"
Framed-IP-Address = 192.168.1.100
计费处理逻辑:
- 服务端需实现
AccountingHandler
记录会话开始/结束时间、流量统计到数据库。
6. 结果验证
- 数据库检查:查看
sys_radius_accounting
表中是否生成计费记录。 - 日志分析:
INFO c.r.r.handler.AccountingHandler - 用户 testuser 会话开始,ID: 123456
四、常见错误排查
错误现象 | 可能原因 | 解决方案 |
---|---|---|
NAS not found | NAS设备IP或密钥未配置 | 检查 sys_nas 表数据 |
User is disabled | 用户状态为“停用” | 在若依后台启用用户 |
Invalid shared secret | NAS密钥与服务器配置不一致 | 同步 sys_nas.secret 与配置文件 |
No response from server | 端口未开放或服务未启动 | 检查防火墙规则,确认服务监听端口 |
五、扩展场景实现
1. 动态带宽控制
- 数据库扩展:
ALTER TABLE sys_user_radius ADD COLUMN bandwidth_limit VARCHAR(20);
- 修改属性工厂类:
public class CustomAttributeFactory { public static void addAttributes(RadiusPacket response, UserDetails user) { // 查询用户带宽限制 String limit = userRadiusMapper.selectBandwidth(user.getUsername()); response.addAttribute("Mikrotik-Rate-Limit", limit); // 示例:Mikrotik设备属性 } }
2. 多因素认证(MFA)
- 集成短信/OTP:
public class RuoyiRadiusHandler { public RadiusPacket handlePacket(RadiusPacket request) { // 认证通过后检查是否需二次验证 if (mfaService.isMfaRequired(user)) { response.addAttribute("Reply-Message", "请输入短信验证码"); response.setCode(RadiusPacket.ACCESS_CHALLENGE); // 返回挑战请求 } } }
六、注意事项
- 密钥安全:共享密钥(
radius.server.secret
)需加密存储,禁止明文暴露。 - NAS设备管理:定期审计
sys_nas
表,禁用未授权的设备。 - 用户状态同步:若依后台禁用用户后,需实时生效(可结合Redis缓存失效机制)。
通过以上实现,Ruoyi-Radius 模块可为企业级网络提供完整的 AAA 服务,并支持灵活的策略扩展。