对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);
    }
}

评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值