类 LoginService

java.lang.Object
net.sohelp.boot.admin.core.service.login.LoginService

@Service public class LoginService extends Object
登录用户验证服务

依据网络安全等级保护(等保)相关要求实现:

  • 身份鉴别:密码复杂度校验(长度、大小写、数字、特殊字符)
  • 登录失败处理:连续失败 N 次锁定账户,超时后自动解锁
  • 密码有效期:定期强制更换,默认 90 天
  • 密码历史:禁止与最近 N 次历史密码重复
  • 弱密码限制:密码不能与用户名相同

相关系统配置项(system.config.pwd_rules):

   min_length          最小密码长度,默认 8(等保要求)
   contain_number      是否必须含数字,默认 false
   contain_lower       是否必须含小写字母,默认 false
   contain_upper       是否必须含大写字母,默认 false
   contain_special     是否必须含特殊字符,默认 false
   pwd_change_interval 密码有效天数,默认 90,0 表示不限制
   max_login_fail      最大连续失败次数,默认 5,0 表示不限制
   login_lock_minutes  锁定时长(分钟),默认 30
   pwd_history_count   禁止重用的历史密码条数,默认 3,0 表示不限制
 
从以下版本开始:
2023/12/22
作者:
AaronFung
  • 构造器详细资料

    • LoginService

      public LoginService()
  • 方法详细资料

    • validate

      @Comment("\u6839\u636e\u7528\u6237\u3001\u5bc6\u7801\u3001\u79df\u6237\u9a8c\u8bc1\u7528\u6237\u662f\u5426\u5b58\u5728") public boolean validate(long tenantID, String loginName, String password) throws SQLException, BusiException
      根据用户、密码、租户验证用户身份,并执行等保登录失败处理机制
      参数:
      tenantID - 租户 ID
      loginName - 登录名
      password - 明文密码
      返回:
      true 表示验证通过
      抛出:
      SQLException - 数据库异常
      BusiException - 账户锁定或凭据错误时抛出
    • validate

      @Comment("\u6839\u636e\u7528\u6237\u548c\u5bc6\u7801\u9a8c\u8bc1\u7528\u6237\u662f\u5426\u5b58\u5728") public boolean validate(String loginName, String password) throws SQLException, BusiException
      根据用户和密码验证用户身份(忽略租户),并执行等保登录失败处理机制
      参数:
      loginName - 登录名
      password - 明文密码
      返回:
      true 表示验证通过
      抛出:
      SQLException - 数据库异常
      BusiException - 账户锁定或凭据错误时抛出
    • changePassword

      @Comment("\u4fee\u6539\u7528\u6237\u5bc6\u7801") public boolean changePassword(String loginName, String newPassword, String oldPassword) throws SQLException, BusiException
      修改用户密码

      执行顺序:

      1. 验证新密码符合复杂度规则
      2. 验证新密码不与用户名相同
      3. 验证旧密码正确
      4. 验证新密码不在历史密码记录中
      5. 更新密码并记录修改时间,同步写入历史
      参数:
      loginName - 登录名
      newPassword - 新密码(明文)
      oldPassword - 旧密码(明文),用于身份验证
      返回:
      true 表示修改成功
      抛出:
      SQLException - 数据库异常
      BusiException - 规则校验失败时抛出
    • forceChangePwd

      @Comment("\u5f3a\u5236\u4fee\u6539\u7528\u6237\u5bc6\u7801\uff08\u5bc6\u7801\u8fc7\u671f\u65f6\u4f7f\u7528\uff0c\u4e0d\u9700\u8981\u65e7\u5bc6\u7801\uff09") public boolean forceChangePwd(String loginName, String newPassword) throws SQLException, BusiException
      强制修改用户密码(密码过期场景,无需提供旧密码)

      执行顺序:

      1. 验证新密码符合复杂度规则
      2. 验证新密码不在历史密码记录中
      3. 保存当前密码到历史,更新密码与修改时间
      参数:
      loginName - 登录名
      newPassword - 新密码(明文)
      返回:
      true 表示修改成功
      抛出:
      SQLException - 数据库异常
      BusiException - 规则校验失败时抛出
    • checkPasswordExpire

      @Comment("\u68c0\u67e5\u7528\u6237\u5bc6\u7801\u662f\u5426\u5df2\u8fc7\u671f\u9700\u8981\u5f3a\u5236\u66f4\u6362") public void checkPasswordExpire(String loginName) throws BusiException, SQLException
      检查用户密码是否已过期,过期则强制要求更换(抛出异常)

      读取 pwd_change_interval 配置,pwd_change_time 为 null 时视为从未更换,直接判定过期。

      参数:
      loginName - 登录名
      抛出:
      BusiException - 密码已过期时抛出
      SQLException - 数据库异常
    • validateByRule

      public void validateByRule(String loginName, String password)
      根据系统配置规则校验密码合法性,同时检查密码不能与用户名相同

      校验项:非空、最小长度(等保默认 8)、数字、小写字母、大写字母、特殊字符、不与用户名相同。

      参数:
      loginName - 登录名(用于禁止密码与用户名相同)
      password - 待验证密码
      抛出:
      BusiException - 不满足规则时抛出
    • updateUser

      @Comment("\u4fee\u6539\u7528\u6237\u57fa\u672c\u4fe1\u606f") public boolean updateUser(Map<String,Object> userMap) throws BusiException, SQLException
      更新用户基本信息

      采用白名单字段策略,仅允许更新以下业务字段,系统字段(权限、凭据、状态流转等) 一律不在此方法处理,防止越权修改:

      • nickname - 昵称
      • user_name - 姓名
      • email - 邮箱
      • phone - 手机号
      • address - 地址
      • avatar - 头像
      • introduction - 个人简介
      • org_id - 所属组织
      参数:
      userMap - 包含 id(必填)及待更新字段的用户数据
      返回:
      true 表示更新成功
      抛出:
      BusiException - id 缺失或无可更新字段时抛出
      SQLException - 数据库异常
    • issuePwdExpiredToken

      @Comment("\u7b7e\u53d1\u5bc6\u7801\u8fc7\u671f\u4e34\u65f6\u4ee4\u724c") public String issuePwdExpiredToken(String loginName)
      签发密码过期临时令牌(用于 code=601 的密码修改流程)

      生成一个 UUID 令牌,以 pwd_exp:{token} 为 key 将登录名写入 VerifyCodeCache(5 分钟有效)。 前端携带此 token 调用 /changePwd 接口完成密码修改。

      参数:
      loginName - 登录名
      返回:
      临时令牌字符串
    • consumePwdExpiredToken

      @Comment("\u9a8c\u8bc1\u5bc6\u7801\u8fc7\u671f\u4e34\u65f6\u4ee4\u724c\uff0c\u8fd4\u56de\u767b\u5f55\u540d") public String consumePwdExpiredToken(String pwdToken)
      验证密码过期临时令牌并返回对应的登录名
      参数:
      pwdToken - 临时令牌
      返回:
      登录名,令牌不存在或已过期时返回 null
    • revokePwdToken

      @Comment("\u64a4\u9500\u5bc6\u7801\u8fc7\u671f\u4e34\u65f6\u4ee4\u724c") public void revokePwdToken(String pwdToken)
      撤销密码过期临时令牌(密码修改成功后调用)
      参数:
      pwdToken - 临时令牌
    • saveLoginLog

      @Comment("\u5199\u5165\u767b\u5f55\u65e5\u5fd7") public void saveLoginLog(String username, String nickname, Integer type, String comments, Long tenantId, jakarta.servlet.http.HttpServletRequest request) throws SQLException
      写入登录日志到 pb_login_record 表

      含 24 小时内累计失败次数(等保 1.2.2 暴力破解检测), 供 sohelp-dev / sohelp-demo 及其他模块共用。

      参数:
      username - 登录名
      nickname - 昵称
      type - 日志类型(0=成功, 1=失败, 2=退出)
      comments - 备注
      tenantId - 租户 ID
      request - HTTP 请求(用于获取 IP、UA 信息)
      抛出:
      SQLException - 数据库异常
    • getLastLoginInfo

      @Comment("\u67e5\u8be2\u7528\u6237\u4e0a\u6b21\u767b\u5f55\u4fe1\u606f\uff08IP / \u65f6\u95f4 / \u8bbe\u5907\uff09") public Map<String,Object> getLastLoginInfo(String username)
      查询用户上一次成功登录的记录(跳过本次,取倒数第二条)

      在本次登录日志已写入后调用,返回上次登录的 ip、时间、平台(os)、设备(device)、浏览器(browser)。 若历史中只有本次登录或表不存在,则返回 null。

      参数:
      username - 登录名
      返回:
      上次登录信息 Map,字段:ip / create_time / os / device / browser;首次登录返回 null
    • getToken

      @Comment("\u7b7e\u53d1\u8bbf\u95ee\u4ee4\u724c\u5e76\u8bb0\u5f55\u767b\u5f55\u65e5\u5fd7\uff08\u7b2c\u4e09\u65b9\u767b\u5f55\u4f7f\u7528\uff09") public String getToken(Map<String,Object> userMap, jakarta.servlet.http.HttpServletRequest request) throws SQLException
      签发访问令牌并记录登录成功日志(供第三方登录控制器统一调用)

      按用户表 token_expired 字段(秒)设置令牌有效期,0 表示永不过期。 同时将 user_no 写入 Session 以兼容旧版会话读取逻辑。

      参数:
      userMap - 用户信息 Map(来自 pb_users select *)
      request - HTTP 请求,用于获取 IP/UA、写 Session
      返回:
      JWT/Sa-Token 访问令牌字符串
      抛出:
      SQLException - 数据库异常(写登录日志)