对Security登录的spring boot项目进行日志管理

  在构思操作之前,我的想法是使用AOP进行切面操作,但是Security整合了一系列的登录操作和结果,我对其不是很熟,没有时间去深究,就参考了大佬的博客。
Spring Security 中基于ApplicationListener记录用户登录日志(含登录成功和登录失败)_springsecurity登录日志_CHQIUU的博客-CSDN博客
    选择使用了监听器,但是获取Request的时候出了问题,因为要获得登录人的IP和浏览器版本等等的信息。
但是实际使用

Objects.requireNonNull(( (ServletRequestAttributes) RequestContextHolder.getRequestAttributes()

以及类似的都获取不到request
后面学到了再用监听器监听request,并存到线程里
我选择是提前获取所需信息,并存到线程里

@Component
public class WebRequestListener implements ServletRequestListener {

    public static final ThreadLocal<String> ipHolder = new ThreadLocal<>();
    public static final ThreadLocal<String> browserHolder = new ThreadLocal<>();
    public static final ThreadLocal<String> osHolder = new ThreadLocal<>();

    @Override
    public void requestInitialized(ServletRequestEvent event) {
        HttpServletRequest request = (HttpServletRequest) event.getServletRequest();

        String userAgent = request.getHeader("User-Agent");
        String remoteAddr = ServletUtil.getClientIP(request, "");
//        解析agent字符串
        UserAgent userAgent1 = UserAgent.parseUserAgentString(userAgent);
//        获取浏览器对象
        Browser browser = userAgent1.getBrowser();
//        获取操作系统对象
        OperatingSystem operatingSystem = userAgent1.getOperatingSystem();
//        System.out.println("浏览器名:" + browser.getName());
//        System.out.println("浏览器类型:" + browser.getBrowserType());
//        System.out.println("浏览器家族:" + browser.getGroup());
//        System.out.println("浏览器生产厂商:" + browser.getManufacturer());
//        System.out.println("浏览器使用的渲染引擎:" + browser.getRenderingEngine());
//        System.out.println("浏览器版本:" + userAgent1.getBrowserVersion());
//        System.out.println("操作系统名:" + operatingSystem.getName());
//        System.out.println("访问设备类型:" + operatingSystem.getDeviceType());
//        System.out.println("操作系统家族:" + operatingSystem.getGroup());
//        System.out.println("操作系统生产厂商:" + operatingSystem.getManufacturer());
        ipHolder.set(remoteAddr);
        browserHolder.set(browser.getName());
        osHolder.set(operatingSystem.getName());
    }
}

然后在上述链接中大佬的监听器增加自己需要的属性,并save到数据库中就行了

import com.blb.pro.entity.Logininfor;
import com.blb.pro.service.LogininforService;
import com.blb.pro.util.ListenerUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.security.authentication.event.*;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;


@Component
public class AuthenticationFailureListener implements
        ApplicationListener<AbstractAuthenticationFailureEvent>{

    @Autowired
    private LogininforService logininforService;

    @Override
    public void onApplicationEvent(AbstractAuthenticationFailureEvent event) {
        String message;
        if (event instanceof AuthenticationFailureBadCredentialsEvent) {
            //提供的凭据是错误的,用户名或者密码错误
            message = "提供的凭据是错误的,用户名或者密码错误";
        } else if (event instanceof AuthenticationFailureCredentialsExpiredEvent) {
            //验证通过,但是密码过期
            message = "验证通过,但是密码过期";
        } else if (event instanceof AuthenticationFailureDisabledEvent) {
            //验证过了但是账户被禁用
            message = "验证过了但是账户被禁用";
        } else if (event instanceof AuthenticationFailureExpiredEvent) {
            //验证通过了,但是账号已经过期
            message = "验证通过了,但是账号已经过期";
        } else if (event instanceof AuthenticationFailureLockedEvent) {
            //账户被锁定
            message = "账户被锁定";
        } else if (event instanceof AuthenticationFailureProviderNotFoundEvent) {
            //配置错误,没有合适的AuthenticationProvider来处理登录验证
            message = "配置错误";
        } else if (event instanceof AuthenticationFailureProxyUntrustedEvent) {
            // 代理不受信任,用于Oauth、CAS这类三方验证的情形,多属于配置错误
            message = "代理不受信任";
        } else if (event instanceof AuthenticationFailureServiceExceptionEvent) {
            // 其他任何在AuthenticationManager中内部发生的异常都会被封装成此类
            message = "内部发生的异常";
        } else {
            message = "其他未知错误";
        }
        // 登录账号
        String username = (String) event.getAuthentication().getPrincipal();
        //状态码
        String status = "1";
        //操作时间
        LocalDateTime time = LocalDateTime.now();

        Logininfor loginLog = ListenerUtils.outAndGetLog(username, message, time, status);
        logininforService.save(loginLog);
    }

}

这里编写了几个工具类 因为在登录成功和登录失败处 除了获取msg信息以为要做的事情不同
ListenerUtils从线程获取ip,浏览器,系统版本  
我们传入 用户名 提示信息  时间 状态码
获得对象  并存入
然后失败监听器也差不多的操作
 

import com.blb.pro.entity.Logininfor;
import com.blb.pro.service.LogininforService;
import com.blb.pro.util.ListenerUtils;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.security.authentication.event.AuthenticationSuccessEvent;
import org.springframework.security.core.userdetails.User;
import org.springframework.stereotype.Component;

import javax.servlet.annotation.WebListener;
import java.time.LocalDateTime;



/**
 * 用户登录成功监听器事件
 *
 * @author chqiuu
 */
@Slf4j
@Component
@WebListener
public class AuthenticationSuccessListener
        implements ApplicationListener<AuthenticationSuccessEvent> {
    @Autowired
    private LogininforService logininforService;

    @Override
    public void onApplicationEvent(AuthenticationSuccessEvent event) {
        /*info_id 访问ID
         *user_name 用户账号 user.
         * ipaddr 登录IP地址
         * login_location  登录地点  判断ip
         * browser 浏览器类型   获取
         * os 操作系统    获取
         * status 登录状态(0成功 1失败) 0
         * msg 提示消息  登录成功
         * login_time 访问时间
         */
        //用户通过输入用户名和密码登录成功
        // 登录账号
        User user = (User) event.getAuthentication().getPrincipal();
        String username = user.getUsername();
        String msg ="登录成功";
        String status = "0";
        LocalDateTime time = LocalDateTime.now();

        Logininfor loginLog = ListenerUtils.outAndGetLog(username, msg, time, status);
        logininforService.save(loginLog);
    }


}

与此同时我还要监听退出的状态,便模仿前面的监听器写了一下

import com.blb.pro.entity.Logininfor;
import com.blb.pro.service.LogininforService;
import com.blb.pro.util.ListenerUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.security.authentication.event.LogoutSuccessEvent;
import org.springframework.security.core.userdetails.User;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;

import static com.blb.pro.listener.WebRequestListener.*;
@Component
public class LogoutSuccessListener implements ApplicationListener<LogoutSuccessEvent> {
    @Autowired
    private LogininforService logininforService;
    @Override
    public void onApplicationEvent(LogoutSuccessEvent event) {

        User user = (User) event.getAuthentication().getPrincipal();
        String username = user.getUsername();
        String msg ="退出成功";
        String status = "0";
        LocalDateTime time = LocalDateTime.now();

        Logininfor loginLog = ListenerUtils.outAndGetLog(username, msg, time, status);
        logininforService.save(loginLog);
    }
}

到此,我们就完成了对Security登录结果的操作日志记录
根据业务需求,我们适当调整自己的工具类就好

-- 2023.9.5 
应博友的需求,就把工具类贴一下,其实写的不是很好,就是把重复方法收集起来,让代码规范点,如果大家有更好的建议可以指出。
 

import com.blb.pro.entity.Logininfor;


import java.time.LocalDateTime;

import static com.blb.pro.listener.WebRequestListener.*;

public class ListenerUtils {

    static String ip = ipHolder.get();
    static String os = osHolder.get();
    static String browser = browserHolder.get();
    //可以自己定义一个ip工具类来接收ip,自己定义哪种是内网,哪种公网,判断属性之后后面使用
    static String ipType = IPUtils.isPublicAndPrivate(ip);

    public static void outputLog(String username, String msg, LocalDateTime loginTime, String status) {
        System.out.println("监听器 ***************** 监听器");
        System.out.println(username);
        System.out.println("ip为" + ip);
        System.out.println("类型:" + ipType);
        System.out.println("浏览器为" + browser);
        System.out.println("操作系统为" + os);
        System.out.println("访问时间为:" + loginTime);
        System.out.println("操作msg为:" + msg);
        System.out.println("状态码为:" + status);

    }

    public  static  Logininfor getLog(String username, String msg, LocalDateTime loginTime, String status) {
        Logininfor loginLog = new Logininfor();
        loginLog.setUserName(username);
        loginLog.setIpaddr(ip);
        loginLog.setLoginLocation(ipType);
        loginLog.setBrowser(browser);
        loginLog.setOs(os);
        loginLog.setStatus(status);
        loginLog.setMsg(msg);
        loginLog.setLoginTime(loginTime);
        return loginLog;
    }

    public static  Logininfor outAndGetLog(String username, String msg, LocalDateTime loginTime, String status) {
        outputLog(username, msg, loginTime, status);
       return getLog(username, msg, loginTime, status);
    }
}

  • 11
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
Spring Boot 是一个用于创建独立的、基于 Spring 的应用程序的开发框架。它提供了许多开箱即用的功能,使得开发人员可以快速构建和部署应用程序。而Spring SecuritySpring 生态系统中的一个强大的安全框架,可以用于保护 Web 应用程序免受各种安全威胁。 Vue 是一个用于构建用户界面的 JavaScript 框架,它提供了一套功能强大的工具和组件,使得开发人员可以轻松构建交互性的前端应用程序。 MySQL 是一个流行的关系型数据库管理系统,广泛用于存储和管理大量的数据。 在一个包含 Spring BootSpring Security、Vue 和 MySQL 的项目中,可以实现以下功能和特性: 1. 用户身份验证和授权:通过 Spring Security 可以实现用户的身份验证和授权的功能,保护后端 API 只允许授权的用户访问。 2. 前后端分离:使用 Vue 作为前端框架,通过 AJAX 或 WebSocket 与后端进行通信,实现前后端的解耦和独立开发。 3. RESTful API:使用 Spring Boot 提供的特性,可以轻松地实现 RESTful API,提供对数据的增删改查操作。 4. 数据库操作:通过集成 MySQL 数据库,可以进行数据的持久化存储和管理,使用 Spring Data JPA 简化数据库操作。 5. 安全配置:通过 Spring Security 可以对 Web 应用程序进行安全配置,例如限制某些 URL 的访问权限、防止 CSRF 攻击等。 6. 日志记录:使用 Spring Boot 自带的日志框架,可以对应用程序的运行情况进行日志记录和监控。 综上所述,结合 Spring BootSpring Security、Vue 和 MySQL,可以构建一个安全可靠的前后端分离项目,实现用户身份验证和授权、数据存储和管理等各种功能。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值