Jenkins单点登录插件开发

Jenkins插件开发篇

前言

​ 想通过第三方平台直接能够单点登录到Jenkins,Jenkins本身是支持单点登录的,并且在Jenkins插件库中有提供相关的认证插件可以安装使用,但是并不能够完全适用,因为现有插件实现的单点是针对谷歌、GitLab等平台,想要让自研平台能够实现单点登录Jenkins,需要单独开发一个插件。

开发

​ 想要开发Jenkins单点登录的认证插件,需要知道两个类,SecurityRealm、AbstractAuthenticationToken,SecurityRealm是用于进行认证操作,AbstractAuthenticationToken可以知道是用于保存认证token信息的。

在SecurityRealm中有两个比较重要的方法,我们通过继承这个类,然后在这个类里面实现这两个方法的业务逻辑,到时候在进行单点认证时Jenkins会调用这两个方法,下面简单说明下这两个方法的作用。

TestSecurityRealm.java

public class ITSMSecurityRealm extends SecurityRealm {
    //这个方法是生成请求单点认证服务的code值地址,并重定向到这个地址,在请求授权码之前认证服务器会进行身份校验,只有过了认证服务器的身份认证之后才能够到重定向到授权码地址,所以可以在重定向前完成单点服务器的认证
    public HttpResponse doCommenceLogin(StaplerRequest request, @Header("Referer") final String referer) throws IOException{

    }

    //这个方法是用于单点服务器回调地址,所以在单点认证服务器那边配置的回调地址需要配置为这个接口地址,然后通过request对象取url中的授权码,然后通过授权码去请求授权服务器颁布凭证,同理,通过授权码获取凭证的接口一样也需要过授权服务器的身份认证,所以可以在请求前先完成身份认证,获取到授权服务器的凭证后将用户认证信息保存在上下文中用于下次请求
    public HttpResponse doFinishLogin(StaplerRequest request) throws IOException {

    }
}

继承AbstractAuthenticationToken类用于存储请求授权服务器响应的凭证然后将其保存在Jenkins服务器上下文中。

安装插件

开发完业务逻辑后,将代码打包为.hpi后缀的插件,通过Jenkins页面方式进行安装开发的认证插件。

在Jenkins仪表盘找到系统管理
在这里插入图片描述

下拉找到插件管理
在这里插入图片描述

选择高级设置

在这里插入图片描述

下拉找到安装插件

在这里插入图片描述

选择打包好的hpi文件,点击部署,等待安装完成,然后重启Jenkins。

至此,插件安装完成。

配置

插件安装完成后还不会直接生效,需要配置单点登录的信息。

在仪表盘中找到系统管理,进入系统管理

在这里插入图片描述

下拉找到全局安全配置

在这里插入图片描述

安全域由Jenins自带数据库换成插件
在这里插入图片描述

然后下方会显示你插件中定义需要填写的参数信息,填写完后点击应用即可

在这里插入图片描述

配置完毕

补充

配置页面中的内容我们能够定义,只需要在resource目录下编写一个jelly的文件,然后在里面定义好需要填写的参数,但这个文件存放的路径是有说法的,路径需要对上SecurityRealm这个类的路径,不然是读取不到的。

读取这个配置参数的方法在SecurityRealm这个类中一个静态内部类的的方法。

在SecurityRealm这个类中定义个静态内部类实现Converter这个类,实现里面marshal方法实现配置文件读取。

public static final class ConverterImpl implements Converter {

        @Override
        public boolean canConvert(Class type) {
            return type == ITSMSecurityRealm.class;
        }

        @Override
        public void marshal(Object source, HierarchicalStreamWriter writer, MarshallingContext context) {

            ITSMSecurityRealm realm = (ITSMSecurityRealm) source;

            //这个"itsmUrl"需要与jelly文件中的属性名映射上
            writer.startNode("itsmUrl");
            writer.setValue(realm.getItsmUrl());
            writer.endNode();

            writer.startNode("clientID");
            writer.setValue(realm.getClientID());
            writer.endNode();

            writer.startNode("secretClientSecret");
            writer.setValue(realm.getSecretClientSecret().getEncryptedValue());
            writer.endNode();

            writer.startNode("callbackUrl");
            writer.setValue(realm.getCallbackUrl());
            writer.endNode();
        }

        @Override
        public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {

            String node = reader.getNodeName();

            ITSMSecurityRealm realm = new ITSMSecurityRealm();

            reader.moveDown();
            node = reader.getNodeName();
            String value = reader.getValue();
            setValue(realm, node, value);
            reader.moveUp();

            reader.moveDown();
            node = reader.getNodeName();
            value = reader.getValue();
            setValue(realm, node, value);
            reader.moveUp();

            reader.moveDown();
            node = reader.getNodeName();
            value = reader.getValue();
            setValue(realm, node, value);
            reader.moveUp();

            reader.moveDown();
            node = reader.getNodeName();
            value = reader.getValue();
            setValue(realm, node, value);
            reader.moveUp();

            if (reader.hasMoreChildren()) {
                reader.moveDown();

                node = reader.getNodeName();

                value = reader.getValue();

                setValue(realm, node, value);

                reader.moveUp();
            }
            return realm;
        }

        private void setValue(ITSMSecurityRealm realm, String node, String value) {

            if (node.equalsIgnoreCase("itsmUrl")) {
                realm.setItsmUrl(value);
            } else if (node.equalsIgnoreCase("clientid")) {
                realm.setClientID(value);
            } else if (node.equalsIgnoreCase("clientsecret")) {
                realm.setClientSecret(value);
            } else if (node.equalsIgnoreCase("secretclientsecret")) {
                realm.setSecretClientSecret(Secret.fromString(value));
            } else if (node.equalsIgnoreCase("callbackUrl")) {
                realm.setCallbackUrl(value);
            } else {
                throw new ConversionException("invalid node value = " + node);
            } 
        }
    }

        }
    }
参考

相关案例在github上面有很多,本文只是大致解释其中逻辑,如果需要案例可以参考以下几个

mallowlabs/redmine-oauth-plugin: A Jenkins Plugin that support authentication via Redmine OAuth Provider Plugin (github.com)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不爱敲代码_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值