分析解读一次TinyRadius运行流程 入门:步骤分明,简洁明了

radius项目下载地址

https://github.com/ctran/TinyRadius

radius资源网

http://tinyradius.sourceforge.net/

 

你必须要知道的

 

  • JavaAPI----radius底层采用的是DatagramSocket进行报文传送的。而DatagramSocket 默认是阻塞式接收消息。

  • server端监听认证端口1812,记账端口1813, 阻塞式等待client端发来报文。在此之前会对报文进行一系列验证,验证通过会给予报文响应。     

  

开始分析

 

Radius客户端想要连接到服务端至少需要什么条件?

 

  四个条件:

       主机: 

          例如 localhost,

            密钥: 

          例如 testing123

        用户名: 

          例如 mw

                     密码:    

          例如 test

    

 

server端开始

 

创建一个服务端并且启动

 

RadiusServer server = new RadiusServer();
server.start(true,true);
 

server.start()方法传入的参数是什么意思?

 

  是否让服务端开启对认证端口 1812 ,记账端口1813的监听。

  我们肯定是要开启的,所以传入true;

 

为了更直观的看到客户端传来的 密钥、密码、用户名等信息,我们重写了RadiusServer的三个方法

  分别是

     getSharedSecre  获取客户端密钥

       getUserPassword 获取客户端密码

       accessRequestReceived  获取客户端传来的报文

 

如下:

 

RadiusServer server = new RadiusServer() {
        (1):
        public String getSharedSecret(InetSocketAddress client) {
                return null;
        }
        (2):
        public String getUserPassword(String userName) {
        
        }
        (3):
        public RadiusPacket accessRequestReceived(AccessRequest accessRequest, InetSocketAddress client) throws RadiusException {
        
        }
    }
    
    server.start(true, true);

 

server.start()方法做了一件什么事?

 

    截取该方法部分代码,清晰明了,如下:

       判断参数是否为true

       是true启动一个线程

       线程只做了一件事:调用listenAuth()方法

 

public void start(boolean listenAuth, boolean listenAcct) {

    if (listenAuth) {
            new Thread() {
                public void run() {
                    setName("Radius Auth Listener");
                    try {
                        log.info("启动 AuthListener 端口:{},调用listenAuth() " ,getAuthPort());
                        listenAuth();  //调用 listenAuth()
                        logger.info("RadiusAuthListener is being terminated");
                    }
                    catch (Exception e) {
                        e.printStackTrace();
                        logger.fatal("auth thread stopped by exception", e);
                    }
                    finally {
                        authSocket.close();
                        logger.debug("auth socket closed");
                    }
                }
            }.start();
        }
        
        //..........
}

 

listenAuth()方法做了一件什么事?

      get到authsocket;

      调用listen()方法;

 

protected void listenAuth() throws SocketException {
   listen(getAuthSocket());  //拿到socket 传给listen()
}
 

getAuthSocket()方法做了一件什么事?如下

 

  1. 它帮我们创建了一个阻塞式socket;

  2. 并且给socket绑定端口,1812或者1813.

 

protected DatagramSocket getAuthSocket() throws SocketException {
        if (authSocket == null) {
            if (getListenAddress() == null)
                authSocket = new DatagramSocket(getAuthPort());
            else
                authSocket = new DatagramSocket(getAuthPort(), getListenAddress());
            authSocket.setSoTimeout(getSocketTimeout());
        }
        return authSocket;
    }

 

核心方法listen()做了一件什么事?

 

接收socket消息。

protected void listen(final DatagramSocket s) {
    
        while (true) {
            
            s.receive(packetIn);
            //.....
        }
}

接收完消息它会对消息进行一系列的check,这是listen 下半部分的关键性代码。

if (executor == null) {
                    log.info("报文接收线程为null,直接调用processRequest(s, packetIn);");
                    log.info("调用processRequest方法");
                    processRequest(s, packetIn);
                }
                else {
                    log.info("报文接收线程存在,submit Runnable(run(processRequest(s, packetIn)))");
                    executor.submit(new Runnable() {
                        
                        @Override
                        public void run() {
                            processRequest(s, packetIn);
                        }
                        
                    });
                }

 

server端结束!给予客户端响应的方法processRequest()

 

它做了以下操作

  1. 判断密钥

  2. 对报文进行解码处理

  3. 对请求报文copy,随后将响应报文添加其上

  4. send(packet) 发送报文

 截取部分代码如下:

  //一系列check  首先检测密钥 
if (secret == null) { log.info("processRequest():secret不存在!"); if (logger.isInfoEnabled()) logger.info("ignoring packet from unknown client " + remoteAddress + " received on local address " + localAddress); return; } log.info("makeRadiusPacket(),处理报文(解码处理等)."); // parse packet final RadiusPacket request = makeRadiusPacket(packetIn, secret, RadiusPacket.UNDEFINED); if (logger.isInfoEnabled()) logger.info("received packet from " + remoteAddress + " on local address " + localAddress + ": " + request); // handle packet logger.trace("about to call RadiusServer.handlePacket()"); log.info("handlePacket(),构建radius响应报文(中间会将请求报文copy并且拼接返回报文)"); final RadiusPacket response = handlePacket(localAddress, remoteAddress, request, secret); // send response if (response != null) { log.info("radius报文转化为socket--》》send()"); if (logger.isInfoEnabled()) logger.info("send response: " + response); final DatagramPacket packetOut = makeDatagramPacket(response, secret, remoteAddress.getAddress(), packetIn.getPort(), request); s.send(packetOut); }

 

 

client端开始

 

客户端做的事情很少

  1. new client

  2. 传入4个关键参数

  3. 建立报文

  4. 调用认证与记账方法。

 

参数:localhost testing123 mw test

 

关键点:每个客户端发送报文后,就要阻塞住等待客户端的响应。

socket.send(packetOut);
socket.receive(packetIn);

初始化代码如下:

RadiusClient rc = new RadiusClient(host, shared);
//创建报文
AccessRequest ar = new AccessRequest(user, pass);
    ar.setAuthProtocol(AccessRequest.AUTH_PAP); // or AUTH_CHAP
    ar.addAttribute("NAS-Identifier", "this.is.my.nas-identifier.de");
    ar.addAttribute("NAS-IP-Address", "192.168.0.100");
    ar.addAttribute("Service-Type", "Login-User");
    ar.addAttribute("WISPr-Redirection-URL", "http://www.sourceforge.net/");
    ar.addAttribute("WISPr-Location-ID", "net.sourceforge.ap1");

//调用认证方法
RadiusPacket response = rc.authenticate(ar);


rc.close();

 

posted @ 2021-07-08 15:58  无上仰无  阅读(1353)  评论(0)    收藏  举报