若依框架对接LDAP

news2025/1/10 21:19:46

这里直接使用spring ldap实现认证。

背景在若依框架上对接LDAP

代码

引入依赖

 <!-- 对接ldap -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-ldap</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>com.unboundid</groupId>
            <artifactId>unboundid-ldapsdk</artifactId>
            <scope>test</scope>
        </dependency>

配置参数

spring:  
  ldap:
    urls: ldap://192.168.10.130
    base: dc=example,dc=com
    username: cn=Manager,dc=example,dc=com
    password: your secret

重写LdapTemplate

@Configuration
public class LdapConfiguration {
    @Value("${spring.ldap.urls}")
    private String urls;
    @Value("${spring.ldap.base}")
    private String base;
    @Value("${spring.ldap.username}")
    private String username;
    @Value("${spring.ldap.password}")
    private String password;
    
    private LdapTemplate ldapTemplate;
    
    @Bean
    public LdapContextSource contextSource() {
        LdapContextSource contextSource = new LdapContextSource();
        Map<String, Object> config = new HashMap<>();
        // 处理乱码
        config.put("java.naming.ldap.attributes.binary", "objectGUID");
        
        contextSource.setUrl(urls);
        contextSource.setBase(base);
        contextSource.setUserDn(username);
        contextSource.setPassword(password);
        // 不使用已经创建好连接
        //  如果使用已经创建连接,去验证,总是失败的
        contextSource.setPooled(true);
        contextSource.setBaseEnvironmentProperties(config);
        // 验证必填参数都设置好
        contextSource.afterPropertiesSet();
        return contextSource;
    }
    
    @Bean
    public LdapTemplate ldapTemplate() {
        if (ldapTemplate == null) {
            ldapTemplate = new LdapTemplate(contextSource());
        }
        return ldapTemplate;
    }

LDAP person对象

根据实际LDAP服务的person名和具体属性

@Data
@Entry(base = "ou=People ", objectClasses = "inetOrgPerson")
public class LdapPerson {
    @Id
    @JsonIgnore
    private Name id;
    
    @DnAttribute(value = "uid")
    private String uid;
    
    @Attribute(name = "cn")
    private String cn;
    
    @Attribute(name = "sn")
    private String sn;
    
    @Attribute(name = "mobile")
    private String mobile;

    @Attribute(name = "mail")
    private String mail;
    
    @Attribute(name = "businessCategory")
    private String businessCategory;
    
    @Attribute(name = "departmentNumber")
    private String departmentNumber;
}

若依登入服务适配LDAP

在文件SysLoginService.java中(当前代码是2022.12月)

1.新增ldapValidate()方法(ldap认证,成功则将该账号自动注册)

2.在认证失败报错有"不存在",则调用ldapValidate()方法,再重新认证一次

@Component
public class SysLoginService
{
    private static final Logger logger = LoggerFactory.getLogger(SysLoginService.class);
    
    @Autowired
    private TokenService tokenService;

    @Resource
    private AuthenticationManager authenticationManager;

    @Autowired
    private RedisCache redisCache;
    
    @Autowired
    private ISysUserService userService;

    @Autowired
    private ISysConfigService configService;

    @Resource
    private LdapTemplate ldapTemplate;
    
    @Resource
    private SysDeptMapper sysDeptMapper;
    
    @Resource
    private SysUserRoleMapper sysUserRoleMapper;

    /**
     * 登录验证
     * 
     * @param username 用户名
     * @param password 密码
     * @param code 验证码
     * @param uuid 唯一标识
     * @return 结果
     */
    public String login(String username, String password, String code, String uuid)
    {
        boolean captchaEnabled = configService.selectCaptchaEnabled();
        // 验证码开关
        if (captchaEnabled)
        {
            validateCaptcha(username, code, uuid);
        }
        // 用户验证
        Authentication authentication = null;
        try
        {
            UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, password);
            AuthenticationContextHolder.setContext(authenticationToken);
            // 该方法会去调用UserDetailsServiceImpl.loadUserByUsername
            authentication = authenticationManager.authenticate(authenticationToken);
        }
        catch (Exception e)
        {
            if (e instanceof BadCredentialsException)
            {
                AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
                throw new UserPasswordNotMatchException();
            }
            else if (e.getMessage().contains("不存在"))
            {
                // ldap认证,成功后注册
                ldapValidate(username, password);
                // 再认证一次
                logger.info("用户:{},LDAP认证成功,重新登入!", username);
                UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, password);
                AuthenticationContextHolder.setContext(authenticationToken);
                authentication = authenticationManager.authenticate(authenticationToken);
            }
            else
            {
                AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, e.getMessage()));
                throw new ServiceException(e.getMessage());
            }
        }
        finally
        {
            AuthenticationContextHolder.clearContext();
        }
        AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));
        LoginUser loginUser = (LoginUser) authentication.getPrincipal();
        recordLoginInfo(loginUser.getUserId());
        // 生成token
        return tokenService.createToken(loginUser);
    }
    
    private void ldapValidate(String username, String password) {
        // 当用户不存在,尝试ldap去验证
        logger.info("用户:{},尝试LDAP认证", username);
        try
        {
            LdapQuery query = LdapQueryBuilder.query().where("uid").is(username);
            ldapTemplate.authenticate(query, password);
            LdapPerson person = ldapTemplate.findOne(query, LdapPerson.class);
            // 注册
            SysUser sysUser = new SysUser();
            SysDept dept = new SysDept();
            dept.setDeptName(person.getDepartmentNumber());
            List<SysDept> depts = sysDeptMapper.selectDeptList(dept);
            if (CollectionUtils.isEmpty(depts))
            {
                logger.error(dept.getDeptName() + "不在部门表中,请新增");
                throw new ServiceException(dept.getDeptName() + "不在部门表中,请新增");
            }

            // ldap信息无性别,无法判断. 默认全部设置男
            sysUser.setDeptId(depts.get(0).getDeptId());
            sysUser.setUserName(username);
            sysUser.setNickName(person.getSn());
            sysUser.setPassword(SecurityUtils.encryptPassword(password));
            sysUser.setEmail(person.getMail());
            sysUser.setPhonenumber(person.getMobile());
            sysUser.setCreateBy("ldap");
            // 普通岗位
            sysUser.setPostIds(new Long[]{2L});
            // 普通角色
            sysUser.setRoleIds(new Long[]{2L});
            if (userService.insertUser(sysUser) == 0)
            {
                throw new ServiceException("注册失败,请联系系统管理人员");
            }
            logger.info("用户:{},注册成功!", username);
        }
        catch (EmptyResultDataAccessException e1)
        {
            AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("LDAP不存在账号:" + username)));
            throw new ServiceException(e1.getMessage());
        }
        catch (AuthenticationException e2)
        {
            AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message(username + "账号在LDAP验证失败")));
            throw new ServiceException(e2.getMessage());
        }
    }

    /**
     * 校验验证码
     * 
     * @param username 用户名
     * @param code 验证码
     * @param uuid 唯一标识
     * @return 结果
     */
    public void validateCaptcha(String username, String code, String uuid)
    {
        String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + StringUtils.nvl(uuid, "");
        String captcha = redisCache.getCacheObject(verifyKey);
        redisCache.deleteObject(verifyKey);
        if (captcha == null)
        {
            AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire")));
            throw new CaptchaExpireException();
        }
        if (!code.equalsIgnoreCase(captcha))
        {
            AsyncManager.me().execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error")));
            throw new CaptchaException();
        }
    }

    /**
     * 记录登录信息
     *
     * @param userId 用户ID
     */
    public void recordLoginInfo(Long userId)
    {
        SysUser sysUser = new SysUser();
        sysUser.setUserId(userId);
        sysUser.setLoginIp(IpUtils.getIpAddr(ServletUtils.getRequest()));
        sysUser.setLoginDate(DateUtils.getNowDate());
        userService.updateUserProfile(sysUser);
    }
}

增加一些sql

需要根据实际添加部门,否则新增用户没有部门id。
后续实际配置部门,权限等

insert into sys_dept values(500,  0,   '0',          'example',   0, '达网', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null);insert into sys_dept values(503,  500, '0,500',  '研发部门',   1, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null);
insert into sys_dept values(504,  500, '0,500',  '高级产品经理',   2, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null);
insert into sys_dept values(505,  500, '0,500',  '产品经理',   3, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null);

验证

当账号不存在自动注册

第一次登入:LDAP去认证,认证成功后,注册用户,再登入

第二次登入:直接登入成功

在这里插入图片描述

再次请求:

在这里插入图片描述

若依也可以打开(权限只是设置普通权限)
在这里插入图片描述

参考

spring ldap:Spring LDAP Reference

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

React-Native 开发实用指南|环信学院

本文主要介绍 React-Native 的实际使用经验&#xff0c;对于想要快速入门的同学是有帮助的。 作者 | 佐玉 出品 | CSDN&#xff08;ID&#xff1a;CSDNnews&#xff09; 1、整体介绍 首先说&#xff0c; React-Native 用来做什么&#xff1f;传统的开发中&#xff0c;按照平…

编译原理学习笔记16——语义分析和中间代码生成1

编译原理学习笔记16——语义分析和中间代码生成116.1 中间语言16.2 常用的中间语言形式16.1 中间语言 中间语言的特点和作用 特点 独立于机器复杂性界于源语言和目标语言之间 引入中间语言的优点 使编译程序的结构在逻辑上更为简单明确便于进行与机器无关的代码优化工作易于移…

力扣刷题|110.平衡二叉树、257. 二叉树的所有路径、404.左叶子之和

LeetCode 110.平衡二叉树 题目链接&#x1f517; LeetCode 110.平衡二叉树 思路 递归三部曲分析&#xff1a; 明确函数的形参和返回值 参数&#xff1a;当前传入结点 返回值&#xff1a;以当前传入结点为根节点的树的高度 那么如何标记左右子树是否差值大于1呢&#xff…

3、SpringJdbcTemplate声明式事务

JdbcTemplate基本使用 01-JdbcTemplate基本使用-概述(了解) JdbcTemplate是spring框架中提供的一个对象&#xff0c;是对原始繁琐的Jdbc API对象的简单封装。spring框架为我们提供了很多的操作模板类。例如&#xff1a;操作关系型数据的JdbcTemplate和HibernateTemplate&…

在Anaconda中安装pytorch的详细步骤+PyCharm环境配置

前言 深度学习小白一枚&#xff0c;自己的笔记本配置如下&#xff1a; 显卡&#xff1a;NVIDIA GeForce MX150&#xff08;非常垃圾的笔记本显卡&#xff09;固态硬盘 256GCPU&#xff1a;lntel Core™i7-8550UWindows&#xff1a;Windows 11家庭中文版 其他配置&#xff1a;…

代驾app开发开发搭建,代驾系统软件源码

在当下移动互联网发展的今天&#xff0c;大家对于生活的追求更加的趋向于其便捷性&#xff0c;这使得各种各样app被开发出来&#xff0c;出现在我们的生活之中。现在就把近期比较火的代驾app开发开发搭建的一些方案介绍一下。 代驾app产生的原因 代驾行业的快速发展&#xf…

<Java EE 进阶> Spring 创建和使用

目录 1.创建Spring项目 &#xff08;1&#xff09;创建一个Maven项目 &#xff08;2&#xff09;添加 Spring 框架支持&#xff08;spring-context、spring-beans&#xff09; &#xff08;3&#xff09;添加启动类 2.存储 Bean 对象 &#xff08;1&#xff09;创建 Bean …

计算几何知识(其一)

前提 最近闲着没事就看了计算几何的一些知识 构建凸包 Incremental Construction 复杂度为O(n2)原理就是不停蚕食下一个合适的点。判断原理是&#xff0c;第X个极点和前n个极点构成的多边形会有切点S&#xff0c;T。 和这两个点的连线&#xff0c;把原先多边形分成两个区域…

【软件测试】翻了下招聘APP只会点点点,很慌......测试业务?技术?

目录&#xff1a;导读前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09;前言 我猜想大伙的职业生…

从0到0.1学习 maven(二:坐标、依赖和仓库)

该文章为maven系列学习的第二篇 第一篇快速入口&#xff1a;从0到0.1学习 maven(一&#xff1a;概述及简单入门) 第二节&#xff1a;坐标、依赖与仓库坐标依赖依赖范围传递性依赖依赖调解可选依赖依赖排除归类依赖优化依赖仓库路径生成仓库分类本地仓库远程仓库快照部署至远程仓…

Day 16 Enable注解

Springboot中提供了很多Enable开头的注解&#xff0c;这些注解是冬天开启某些功能的&#xff0c;而其底层使用Import注解导入一些配置类&#xff0c;实现Bean的动态加载1 EnableAutoConfigurationTarget(ElementType.TYPE) Retention(RetentionPolicy.RUNTIME) Documented Inhe…

京东十年T8架构师手撕MySQL:手写666页核心知识,超85%问题全解

MySQL是开放源码的关系数据库管理系统&#xff0c;由于 性能高、成本低、可靠性好&#xff0c;成为现在最流行的开源数据库。 MySQL学习指南 笔记包含了3个大章节&#xff0c;13个小章节&#xff1a; 基础篇 MySQL数据类型MySQL运算符MySQL函数MySQL数据库查询语句 核心篇 …

​力扣解法汇总2319. 判断矩阵是否是一个 X 矩阵

目录链接&#xff1a; 力扣编程题-解法汇总_分享记录-CSDN博客 GitHub同步刷题项目&#xff1a; https://github.com/September26/java-algorithms 原题链接&#xff1a;力扣 描述&#xff1a; 如果一个正方形矩阵满足下述 全部 条件&#xff0c;则称之为一个 X 矩阵 &#…

自动化设备ERP系统对企业管理有什么好处?

随着自动化设备制造企业的快速发展&#xff0c;规模和产能的不断扩大&#xff1b;设备也不断增加、品种越来越多&#xff1b;企业信息化建设也不断发展和完善, 自动化设备ERP系统已经成为企业信息化建设的一个有机组成部分。自动化设备管理已进入信息化、数字化时代。自动化设备…

机器学习之求解无约束最优化问题方法(手推公式版)

文章目录前言1. 基础知识1.1 方向导数1.2 梯度1.3 方向导数与梯度的关系1.4 泰勒展开公式1.5 Jacobian矩阵与Hessian矩阵1.6 正定矩阵2. 梯度下降法3. 牛顿法4. 拟牛顿法5. 代码实现结束语前言 本篇博文主要介绍了机器学习里面的常见的求解无约束最优化问题的方法&#xff0c;包…

LeetCode——1664. 生成平衡数组的方案数

一、题目 给你一个整数数组 nums。你需要选择恰好一个下标&#xff08;下标从0开始&#xff09;并删除对应的元素。请注意剩下元素的下标可能会因为删除操作而发生改变。 比方说&#xff0c;如果 nums [6,1,7,4,1] &#xff0c;那么&#xff1a; 选择删除下标 1 &#xff0…

在CentOS-6.9部署apache服务

文章目录一 系统环境二 部署服务2.1 yum安装软件2.2 修改主配置文件2.3 修改防火墙规则2.4 访问测试三 主配置文件参数3.1 主配置文件常规语句3.2 主配置文件日志控制语句3.3 主配置文件的性能控制语句一 系统环境 参数值主机IP10.0.0.100主机名test操作系统版本CentOS releas…

css 过渡动画

目录过渡动画1 css 属性1.1 transform 变换&#xff08;平移旋转缩放&#xff09;1.2 animation 动画1.2.1 keyframes1.3 transition 过渡1.4 比较2 方式2.1 css 伪类2.2 vue <Transition> 组件2.2.1 默认名称2.2.2 自定义名称2.2.3 自定义 class2.2.4 配合 animation2.2…

sqlserver存储过程简单游标示例

test数据库有表如下&#xff1b; 创建一个存储过程&#xff0c;输出姓名和电话&#xff1b; CREATE PROCEDURE printname AS BEGINDECLARE sName varchar(20), phone varchar(20)DECLARE cursor1 CURSOR FOR --定义游标SELECT name,phonenumber from t_student OPEN cursor1 …

OpenStack的“神秘组件” 裸金属(Ironic)管理使用

OpenStack是目前全球部署最广泛的开源云基础架构&#xff0c;在OpenStack中提供的裸金属服务的项目是Ironic。OpenStack的官网主要介绍裸金属的用途在如下5方面&#xff1a; &#xff08;1&#xff09;高性能计算&#xff1b; &#xff08;2&#xff09;无法虚拟化的硬件设备的…