基于ldap实现登录认证

        最近开发的应用需要外协人员实现登录认证,外协人员的密码等信息已经录入到ldap, 需要连接ldap进行登录认证。下面先介绍一下登录的网络旅程图。

一.nginx实现AES加密

nginx请求处理入口(前端请求为json格式)

 location /aes {default_type text/html;content_by_lua_block{local access_filter = require 'resty.aes_auth'local r = access_filter.aes_auth()ngx.header.content_type = "application/json; charset=UTF-8"if r == true then ngx.say([[{"code":200,"message":"Certification         successful!","data":true,"logCode":null}]])else ngx.say([[{"code":401,"message":"Authentication failed!","data":false,"logCode":null }]])endngx.exit(200)}}

 openresty请求认证接口脚本

local aes = require "resty.aes"
local cjson = require("cjson.safe")
local http = require("resty.http")
local key = "abcdefmJTNn}8Z#2`"
local iv = "1234567890123456"local _M = {}
function _M.aes_auth()ngx.req.read_body()local args,err = ngx.req.get_body_data()if (not args) or (err) thenreturn falseendlocal arg_json = cjson.decode(args)local username = arg_json.usernamelocal password = arg_json.passwordif (not username) or (not password) thenreturn falseendlocal cript = aes:new(key, nil, aes.cipher(128, "cbc"), {iv=iv, method=nil})local pwd = cript:encrypt(password)if pwd thenpwd = ngx.encode_base64(pwd)elsereturn falseendlocal httpc = http.new()local requestBody = {username = username,password = pwd}local json_body = cjson.encode(requestBody)local resp,err = httpc:request_uri("http://10.1.1.1:8080", {method = "POST",path = "/ldap/authUser",body = json_body,headers = {  ---header参数["Content-Type"] = "application/json;charset=UTF-8"}})if err thenreturn falseendlocal result = falseif resp thenlocal data = cjson.decode(resp.body).dataif data thenresult = dataendendreturn result
endreturn _M

 二.应用服务调用ldap服务

引入依赖

<!--ldap-->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-ldap</artifactId><version>2.3.12.RELEASE</version>
</dependency>
<!--aes对称加密-->
<dependency><groupId>org.bouncycastle</groupId><artifactId>bcprov-jdk15on</artifactId><version>1.56</version>
</dependency>

AES加密解密工具类,需要注意的是nginx不支持PKCS5Padding填充方式。

package com.xxx.xxx.xxx.util;import java.nio.charset.StandardCharsets;
import java.security.Security;
import java.util.Base64;import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import lombok.extern.slf4j.Slf4j;
import org.junit.platform.commons.util.StringUtils;
import org.springframework.util.Base64Utils;/*** description:AES对称加密工具类** @author: lgq* @create: 2024-01-26 10:03*/
@Slf4j
public class AESUtil {/*** 日志相关*//*** 编码*/private static final String ENCODING = "UTF-8";/*** 算法定义*/private static final String AES_ALGORITHM = "AES";/*** 指定填充方式*/private static final String CIPHER_PADDING = "AES/ECB/PKCS5Padding";//必须使用PKCS7Padding,因为nginx不支持PKCS5Padding填充方式private static final String CIPHER_CBC_PADDING = "AES/CBC/PKCS7Padding";/*** 偏移量(CBC中使用,增强加密算法强度)*/private static final String IV_SEED = "1234567890123456";private static final String RANDOM_SECRET = "abcefmJTNn}8Z#2`";static {// 指定使用bouncycastle包来加密, 引入目的就是为了支持AES/CBC/PKCS7PaddingSecurity.addProvider(new BouncyCastleProvider());}public static String getRandomSecret() {return RANDOM_SECRET;}/*** AES加密** @param content 待加密内容* @param aesKey  密码* @return*/public static String encrypt(String content, String aesKey) {if (StringUtils.isBlank(content)) {log.info("AES encrypt: the content is null!");return null;}//判断秘钥是否为16位if (StringUtils.isNotBlank(aesKey) && aesKey.length() == 16) {try {//对密码进行编码byte[] bytes = aesKey.getBytes(ENCODING);//设置加密算法,生成秘钥SecretKeySpec skeySpec = new SecretKeySpec(bytes, AES_ALGORITHM);// "算法/模式/补码方式"Cipher cipher = Cipher.getInstance(CIPHER_PADDING);//选择加密cipher.init(Cipher.ENCRYPT_MODE, skeySpec);//根据待加密内容生成字节数组byte[] encrypted = cipher.doFinal(content.getBytes(ENCODING));//返回base64字符串return Base64Utils.encodeToString(encrypted);} catch (Exception e) {log.info("AES encrypt exception:" + e.getMessage());throw new RuntimeException(e);}} else {log.info("AES encrypt: the aesKey is null or error!");return null;}}/*** 解密** @param content 待解密内容* @param aesKey  密码* @return*/public static String decrypt(String content, String aesKey) {if (StringUtils.isBlank(content)) {log.info("AES decrypt: the content is null!");return null;}//判断秘钥是否为16位if (StringUtils.isNotBlank(aesKey) && aesKey.length() == 16) {try {//对密码进行编码byte[] bytes = aesKey.getBytes(ENCODING);//设置解密算法,生成秘钥SecretKeySpec skeySpec = new SecretKeySpec(bytes, AES_ALGORITHM);// "算法/模式/补码方式"Cipher cipher = Cipher.getInstance(CIPHER_PADDING);//选择解密cipher.init(Cipher.DECRYPT_MODE, skeySpec);//先进行Base64解码byte[] decodeBase64 = Base64Utils.decodeFromString(content);//根据待解密内容进行解密byte[] decrypted = cipher.doFinal(decodeBase64);//将字节数组转成字符串return new String(decrypted, ENCODING);} catch (Exception e) {log.info("AES decrypt exception:" + e.getMessage());throw new RuntimeException(e);}} else {log.info("AES decrypt: the aesKey is null or error!");return null;}}/*** AES_CBC加密** @param content 待加密内容* @param aesKey  密码* @return*/public static String encryptCBC(String content, String aesKey) {if (StringUtils.isBlank(content)) {log.info("AES_CBC encrypt: the content is null!");return null;}//判断秘钥是否为16位if (StringUtils.isNotBlank(aesKey) && aesKey.length() == 16) {try {//对密码进行编码byte[] bytes = aesKey.getBytes(ENCODING);//设置加密算法,生成秘钥SecretKeySpec skeySpec = new SecretKeySpec(bytes, AES_ALGORITHM);// "算法/模式/补码方式"Cipher cipher = Cipher.getInstance(CIPHER_CBC_PADDING);//偏移IvParameterSpec iv = new IvParameterSpec(IV_SEED.getBytes(ENCODING));//选择加密cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);//, iv//根据待加密内容生成字节数组byte[] encrypted = cipher.doFinal(content.getBytes(ENCODING));//返回base64字符串return Base64Utils.encodeToString(encrypted);} catch (Exception e) {log.info("AES_CBC encrypt exception:" + e.getMessage());throw new RuntimeException(e);}} else {log.info("AES_CBC encrypt: the aesKey is null or error!");return null;}}/*** AES_CBC解密** @param content 待解密内容* @param aesKey  密码* @return*/public static String decryptCBC(String content, String aesKey) {if (StringUtils.isBlank(content)) {log.info("AES_CBC decrypt: the content is null!");return null;}//判断秘钥是否为16位if (StringUtils.isNotBlank(aesKey) && aesKey.length() == 16) {try {//对密码进行编码byte[] bytes = aesKey.getBytes(ENCODING);//设置解密算法,生成秘钥SecretKeySpec skeySpec = new SecretKeySpec(bytes, AES_ALGORITHM);//偏移IvParameterSpec iv = new IvParameterSpec(IV_SEED.getBytes(ENCODING));// "算法/模式/补码方式"Cipher cipher = Cipher.getInstance(CIPHER_CBC_PADDING);//选择解密cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);//先进行Base64解码byte[] decodeBase64 = Base64Utils.decodeFromString(content);//根据待解密内容进行解密byte[] decrypted = cipher.doFinal(decodeBase64);//将字节数组转成字符串return new String(decrypted, ENCODING);} catch (Exception e) {log.info("AES_CBC decrypt exception:" + e.getMessage());throw new RuntimeException(e);}} else {log.info("AES_CBC decrypt: the aesKey is null or error!");return null;}}}

ladp配置类

package com.xxx.xxx.xxx.config;import javax.annotation.Resource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.ldap.core.LdapTemplate;
import org.springframework.ldap.core.support.LdapContextSource;/*** description:LdapConfig** @author: lgq* @create: 2024-01-25 10:34*/
@Configuration
public class LdapConfig {@Resourceprivate LdapProperties ldapProperties;@Beanpublic LdapTemplate ldapTemplate() {LdapContextSource contextSource = new LdapContextSource();contextSource.setUrl(ldapProperties.getUrls());contextSource.setBase(ldapProperties.getBase());contextSource.setUserDn(ldapProperties.getUsername());contextSource.setPassword(ldapProperties.getPassword());contextSource.afterPropertiesSet();LdapTemplate ldapTemplate = new LdapTemplate(contextSource);ldapTemplate.setIgnorePartialResultException(true);ldapTemplate.setDefaultTimeLimit(1000);ldapTemplate.setDefaultCountLimit(100);return ldapTemplate;}}package com.xxx.xxx.xxx.config;import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;/*** description:ldapProperties** @author: lgq* @create: 2024-01-25 18:13*/
@Data
@ConfigurationProperties(prefix = "spring.ldap")
public class LdapProperties {/*** ldap服务地址*/private String urls;/*** 用户账号*/private String username;/*** 密码*/private String password;/*** base路径*/private String base;
}yml文件配置:spring:profiles: prodapplication:name: service-xxxldap:urls: "ldap://10.1.1.1:389"password: "xxxxxxxx"username: "cn=xxx.LDAP,ou=xxx,ou=xxx,dc=xxx,dc=xxx"base: "dc=xxx,dc=xxx"

认证服务类

package com.xxx.xxx.xxx.service.impl;import java.nio.charset.StandardCharsets;
import java.util.Base64;import javax.annotation.Resource;import com.xxx.xxx.xxx.service.LdapService;
import com.xxx.xxx.xxx.util.AESUtil;import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.ldap.core.LdapTemplate;
import org.springframework.stereotype.Service;/*** description:LdapServiceImpl** @author: lgq* @create: 2024-01-26 09:26*/
@Service
@Slf4j
public class LdapServiceImpl implements LdapService {@Resourceprivate LdapTemplate ldapTemplate;/*** 验证登录用户的账号密码是否正确* @param username* @param password* @return*/@Overridepublic boolean authLoginUser(String username, String password) {if (ObjectUtils.isEmpty(username) || ObjectUtils.isEmpty(password)) {return false;}/*** aes对password进行解密*/String content = AESUtil.decryptCBC(password, AESUtil.getRandomSecret());if (ObjectUtils.isEmpty(content)) {return false;}String baseDn = "";String filter = "sAMAccountName=" + username;boolean result = false;try {result = ldapTemplate.authenticate(baseDn, filter, content);} catch (Exception ex) {log.error(ex.getMessage(), ex);} catch (Error er) {log.error(er.getMessage(), er);}return result;}}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/248203.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

adb测试冷启动和热启动 Permission Denial解决

先清理日志 adb shell logcat -c 打开手机模拟器中的去哪儿网&#xff0c;然后日志找到包名和MainActivity adb shell logcat |grep Main com.Qunar/com.mqunar.atom.alexhome.ui.activity.MainActivity 把手机模拟器的去哪儿的进程给杀掉 执行 命令 adb shell am start -W…

方法、数组

方法 是语句的集合&#xff0c;在一起执行一个功能 它是解决一类问题的步骤的有序集合 包含于类或对象中 在程序中创建&#xff0c;在其他地方被引用 设计方法的原则&#xff1a;方法的本意是功能块&#xff0c;就是实现某一个功能的语句块的集合。设计时&#xff0c;最好保持…

Vue3+Vite使用Puppeteer进行SEO优化(SSR+Meta)

1. 背景 【笑小枫】https://www.xiaoxiaofeng.com上线啦 资源持续整合中&#xff0c;程序员必备网站&#xff0c;快点前往围观吧~ 我的个人博客【笑小枫】又一次版本大升级&#xff0c;虽然知道没有多少访问量&#xff0c;但我还是整天没事瞎折腾。因为一些功能在Halo上不太好实…

Unity中URP下额外灯角度衰减

文章目录 前言一、额外灯中聚光灯的角度衰减二、AngleAttenuation函数的传入参数1、参数&#xff1a;spotDirection.xyz2、_AdditionalLightsSpotDir3、参数&#xff1a;lightDirection4、参数&#xff1a;distanceAndSpotAttenuation.zw5、_AdditionalLightsAttenuation 三、A…

哪吒汽车与经纬恒润合作升级,中央域控+区域域控将于2024年落地

近日&#xff0c;在2024哪吒汽车价值链大会上&#xff0c;哪吒汽车与经纬恒润联合宣布合作升级&#xff0c;就中央域控制器和区域域控制器展开合作&#xff0c;合作成果将在山海平台新一代车型上发布。 哪吒汽车首席技术官戴大力、经纬恒润副总裁李伟 经纬恒润在智能驾驶领域拥…

Springboot自定义线程池实现多线程任务

1. 在启动类添加EnableAsync注解 2.自定义线程池 package com.bt.springboot.config;import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.concurrent.ThreadPoolTask…

MySQL原理(二)存储引擎(1)概述

一、存储引擎介绍 1、概念&#xff1a; &#xff08;1&#xff09;MySQL中的数据用各种不下同的技术存储在文件中&#xff0c;每一种技术都使用不同的存储机制、索引技巧、锁定水平并最终提供不同的功能和能力&#xff0c;这些不同的技术以及配套的功能在MySQL中称为存储引擎…

@Async结合CompletableFuture实现主线程阻塞,CompletableFuture并发执行任务

Async结合CompletableFuture实现主线程阻塞&#xff0c;CompletableFuture并发执行任务 项目开发中经常会遇到业务需要多任务处理的场景&#xff0c;比如目前我除了的业务就是如此。 我要提供给客户端一个批量查询第三方数据的接口&#xff0c;由于是调用第三方的接口&#xf…

正则表达式 文本三剑客

一 正则表达式&#xff1a; 由一类特殊字符及文本字符所编写的模式&#xff0c;其中有些字符&#xff08;元字符&#xff09;不表示字符字面意义&#xff0c;而表示控制或通配的功能&#xff0c;类似于增强版的通配符功能&#xff0c;但与通配符不同&#xff0c;通配符功能是用…

分享4款不能错过的修改照片尺寸的软件!

在当今这个数字化时代&#xff0c;照片已经成为我们分享生活、表达观点的重要方式。但是&#xff0c;你是否曾遇到过这样的问题&#xff1a;一张精美的照片因为尺寸不合适而无法在朋友圈中展现出最佳效果&#xff1f;不用担心&#xff0c;今天我们就来聊聊那些可以帮助你轻松修…

漏洞原理远程命令执行

漏洞原理远程命令/代码执行 远程命令执行函数&#xff08;Remote Command Execution Function&#xff09;是指在一个网络环境中&#xff0c;通过远程执行命令来控制另一个计算机系统或设备的功能。 远程命令执行函数可以通过网络协议&#xff08;如SSH、Telnet、RPC等&#x…

wpf 数据转换(Bytes 转 KB MB GB)

效果 后端 using ProCleanTool.Model; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Globalization; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Data;namespace P…

SkyWalking+es部署与使用

第一步下载skywalking :http://skywalking.apache.org/downloads/ 第二步下载es:https://www.elastic.co/cn/downloads/elasticsearch 注&#xff1a;skywalking 和es要版本对应&#xff0c;可从下面连接查看版本对应关系&#xff0c;8.5.0为skywalking 版本号 Index of /di…

keepalived+nginx双主热备(有问题私信)

keepalivednginx双主热备 前言keepalivednginx双主热备keepalivednginx双主热备部署安装nginx安装keepalived修改master节点的keepalived配置文件 修改backup节点的keeepalived配置文件配置keepalived主备配置keepalived双主热备 前言 有关keepalived和nginx的一些工作原理&am…

【蓝桥杯日记】复盘篇二:分支结构

前言 本篇笔记主要进行复盘的内容是分支结构&#xff0c;通过学习分支结构从而更好巩固之前所学的内容。 目录 前言 目录 &#x1f34a;1.数的性质 分析&#xff1a; 知识点&#xff1a; &#x1f345;2.闰年判断 说明/提示 分析&#xff1a; 知识点&#xff1a; &am…

RHCE练习3

1.基于域名www.openlab.com可以访问网站内容为 welcome to openlab 2.给该公司创建三个子界面分别显示学生信息&#xff0c;教学资料和缴费网站&#xff0c;基于www.openlab.com/student 网站访问学生信息&#xff0c;www.openlab.com/data网站访问教学资料www.openlab.com/mo…

python之组合数据类型-列表

列表操作 列表增删改查列表增加元素的方法列表删除元素的方法列表修改元素的方法列表查找元素的方法 列表其他常用方法列表的切片用法列表修改排序的方法列表的常用符号、常用函数 列表是什么&#xff1f; 列表是有序集合&#xff0c;列表可以一次性存储几个或几万个元素&#…

海外云手机运营Instagram攻略

Instagram是世界著名的社交媒体平台&#xff0c;有着10亿实时用户&#xff0c;是跨境电子商务的优质流量来源。平台以女性用户为主&#xff0c;购物倾向高&#xff0c;转化率好。它被公认为外贸行业的优质社交媒体流量池。那么&#xff0c;如何使用海外云手机吸引Instagram上的…

网络原理-TCP/IP(1)

应用层 我们之前编写完了基本的java socket, 要知道,我们之前所写的所有代码都在应用层中,都是为了完成某项业务,如翻译等.关于应用层,后面会有专门的讲解,在此处先讲一下基础知识. 应用层对应着应用程序,是程序员打交道最多的一层,调用系统提供的网络api写出的代码都是应用层…

快速上手Git

目录 一、Git概述 二、Git的常用命令 Git全局配置 获取Git仓库 基本概念 本地仓库操作 远程仓库操作 分支操作 标签操作 三、在IDEA中使用Git 在IDEA中配置Git 本地仓库操作 远程仓库操作 分支操作 冲突解决 一、Git概述 Git是一个分布式版本控制工具&…