Series directory

SpringSecurity rights management system actual combat – one, project introduction and development environment preparation

SpringSecurity rights management system practice – two, log, interface documents and other implementation

SpringSecurity rights management system practice – three, the main page and interface implementation

SpringSecurity rights management system actual combat – four, integration of SpringSecurity (part 1)

SpringSecurity rights management system practice – five, integration of SpringSecurity (under)

SpringSecurity authority management system combat – six, SpringSecurity integration JWT

SpringSecurity rights management system practice – seven, deal with some problems

SpringSecurity rights management system practice – eight, AOP record user logs, abnormal logs

SpringSecurity rights management system actual combat – nine, data rights configuration

preface

In fact, the log function was simply implemented in the second, but sometimes we need to log some important function operations, or in the operation of an exception, the need to record an exception log. However, we had to go to the server to query the log to locate the cause of every abnormal occurrence, or maybe build a log collection system (but this project is not considered for the time being). Then we can make a special function to record user operation logs and exception logs, and store the logs in the database for easy query.

First, the final effect

Alter table my_log

User logs and exception logs are stored in this table, which can be distinguished by type. Of course, it can also be split into two tables.

Add dependencies

 		<! --aop-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
        <! -- UserAgentUtils, Browser Info utility class -->
        <dependency>
            <groupId>eu.bitwalker</groupId>
            <artifactId>UserAgentUtils</artifactId>
            <version>1.21</version>
        </dependency>
        <! -- IP2region --ip2region --ip2region --ip2region
        <! -- <dependency>-->
        <! -- <groupId>org.lionsoul</groupId>-->
        <! -- <artifactId>ip2region</artifactId>-->
        <! - < version > 1.7.2 < / version > -- >
        <! -- </dependency>-->
		<! -- Pagination tool -->
        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper-spring-boot-starter</artifactId>
            <version>1.2.13</version>
        </dependency>
        <! - hutool tools - >
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.1.4 ensuring</version>
        </dependency>
Copy the code

4. Tool classes needed

SecurityUtils

/ * * *@author codermy
 * @createTime2020/8/4 * /
public class SecurityUtils {

    /** * Obtain the system user name **@returnSystem user name */
    public static String getCurrentUsername(a) {
        final Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (authentication == null) {
            throw new MyException(ResultCode.UNAUTHORIZED, "Current login status expired");
        }
        UserDetails userDetails = (UserDetails) authentication.getPrincipal();
        return userDetails.getUsername();
    }
    /** * returns an empty string if the current user is not logged in
    public static String getCurrentUserIp(a) {
        final Authentication authentication = SecurityContextHolder.getContext().getAuthentication();

        if (authentication == null) {
            throw new MyException(ResultCode.UNAUTHORIZED, "Current login status expired");
        }
        Object details = authentication.getDetails();
        if(! (detailsinstanceof WebAuthenticationDetails)) {
            return "";
        }
        WebAuthenticationDetails webDetails = (WebAuthenticationDetails) details;
        returnwebDetails.getRemoteAddress(); }}Copy the code

LogUtils

/ * * *@author codermy
 * @createTime2020/8/7 * /
public class LogUtils {
    private static final char SEPARATOR = '_';

    private static final String UNKNOWN = "unknown";
    /** * Obtain the IP address */
    public static String getIp(HttpServletRequest request) {
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        String comma = ",";
        String localhost = "127.0.0.1";
        if (ip.contains(comma)) {
            ip = ip.split(",") [0];
        }
        if  (localhost.equals(ip))  {
            // Get the real IP address of this machine
            try {
                ip = InetAddress.getLocalHost().getHostAddress();
            } catch(UnknownHostException e) { e.printStackTrace(); }}return ip;
    }

    /** * Get browser information *@param request
     * @return* /
    public static String getBrowser(HttpServletRequest request){
        UserAgent userAgent = UserAgent.parseUserAgentString(request.getHeader("User-Agent"));
        Browser browser = userAgent.getBrowser();
        return browser.getName();
    }

    /** * get stack information */
    public static String getStackTrace(Throwable throwable){
        StringWriter sw = new StringWriter();
        try (PrintWriter pw = new PrintWriter(sw)) {
            throwable.printStackTrace(pw);
            returnsw.toString(); }}}Copy the code

RequestHolder

/ * * *@author codermy
 * @createTime2020/8/4 * /
public class RequestHolder {
    /** * Get the HttpServletRequest object *@return* /
    public static HttpServletRequest getHttpServletRequest(a) {
        return((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest(); }}Copy the code

5. Corresponding entity class

This part is omitted, it’s not technical, it’s based on the database, right

Custom annotation classes for operation logs

/ * * *@author codermy
 * @createTime2020/8/4 * /
@Target(ElementType.METHOD)METHOD is annotable at the METHOD level
@Retention(RetentionPolicy.RUNTIME)// At which stage annotations are executed
public @interface MyLog {
    String value(a) default "";
}
Copy the code

Check out this article on Java custom annotations

7. Create a section class

This is actually quite easy to understand, as we learn spring,aop several notifications.

/ * * *@author codermy
 * @createTime2020/8/4 * /
@Component
@Aspect
@Slf4j
public class LogAspect {
    // Inject logService to store logs to the database
    @Autowired
    private MyLogService logService;

    ThreadLocal<Long> currentTime = new ThreadLocal<>();
    /** * Set the operation log pointcut to record the operation log entry into the code at the annotation position */
    @Pointcut("@annotation(com.codermy.myspringsecurityplus.log.aop.MyLog)")
    public void logPoinCut(a) {}/** * Configure surround notification using the pointcut ** registered on the method logPointcut()@param joinPoint join point for advice
     */
    @Around("logPoinCut()")
    public Object saveSysLog(ProceedingJoinPoint joinPoint)throws Throwable{
        Object result;
        currentTime.set(System.currentTimeMillis());// Record the execution time of the method
        result = joinPoint.proceed();
        MyLog log = new MyLog("INFO",System.currentTimeMillis() - currentTime.get());// Define log types
        currentTime.remove();
        HttpServletRequest request = RequestHolder.getHttpServletRequest();
        logService.save(SecurityUtils.getCurrentUsername(), LogUtils.getBrowser(request), LogUtils.getIp(request),joinPoint, log);
        return result;
    }

    /** * Config exception notification **@param joinPoint join point for advice
     * @param e exception
     */
    @AfterThrowing(pointcut = "logPoinCut()", throwing = "e")
    public void logAfterThrowing(JoinPoint joinPoint, Throwable e) {
        MyLog log = new MyLog("ERROR",System.currentTimeMillis() - currentTime.get()); currentTime.remove(); log.setExceptionDetail(LogUtils.getStackTrace(e)); HttpServletRequest request = RequestHolder.getHttpServletRequest(); logService.save(SecurityUtils.getCurrentUsername(), LogUtils.getBrowser(request), LogUtils.getIp(request), (ProceedingJoinPoint)joinPoint, log); }}Copy the code

8. Corresponding methods and interfaces

dao

/ * * *@author codermy
 * @createTime2020/8/8 * /
@Mapper
public interface LogDao {

    /** * Save log *@param log
     */
    @Insert("insert into my_log(user_name,ip,description,params,type,exception_detail,browser,method,time,create_time)values(#{userName},#{ip},#{ description},#{params},#{type},#{exceptionDetail},#{browser},#{method},#{time},now())")
    void save(MyLog log);

    /** * Paging returns all user logs *@paramLogQuery Query condition *@return* /
    List<LogDto> getFuzzyLogByPage( @Param("logQuery") LogQuery logQuery);


    /** * Paging returns all error logs *@paramLogQuery Query condition *@return* /
    List<ErrorLogDto> getFuzzyErrorLogByPage(@Param("logQuery") LogQuery logQuery);


    /** * delete all logs *@paramType Log type */
    @Delete("delete from my_log where type = #{type}")
    void delAllByInfo(String type);
}

Copy the code

LogMapper.xml

<mapper namespace="com.codermy.myspringsecurityplus.log.dao.LogDao">
    <select id="getFuzzyLogByPage" resultType="com.codermy.myspringsecurityplus.log.dto.LogDto">
        SELECT t.user_name,t.ip,t.params,t.description,t.browser,t.time,t.method,t.create_time
        FROM my_log t
        <where>
            <if test="logQuery.logType != null and logQuery.logType  != ''">
                AND t.type = #{logQuery.logType}
            </if>
            <if test="logQuery.userName ! = null and logQuery.userName ! = "">
                AND t.user_name like CONCAT('%', #{logQuery.userName}, '%')
            </if>
        </where>
        ORDER BY t.create_time desc
    </select>

    <select id="getFuzzyErrorLogByPage" resultType="com.codermy.myspringsecurityplus.log.dto.ErrorLogDto">
        SELECT t.user_name,t.ip,t.params,t.description,t.browser,t.exception_detail,t.method,t.create_time
        FROM my_log t
        <where>
            <if test="logQuery.logType != null and logQuery.logType  != ''">
                AND t.type = #{logQuery.logType}
            </if>
            <if test="logQuery.userName ! = null and logQuery.userName ! = "">
                AND t.user_name like CONCAT('%', #{logQuery.userName}, '%')
            </if>
        </where>
        ORDER BY t.create_time desc
    </select>

</mapper>

Copy the code

MyLogServiceImpl

/ * * *@author codermy
 * @createTime2020/8/4 * /
@Service
public class MyLogServiceImpl implements MyLogService {
    @Autowired
    private LogDao logDao;
	// Returns the user log
    @Override
    public Result<LogDto> getFuzzyInfoLogByPage(Integer offectPosition, Integer limit, LogQuery logQuery) {
        Page page = PageHelper.offsetPage(offectPosition,limit);
        List<LogDto> fuzzyLogByPage = logDao.getFuzzyLogByPage(logQuery);
        return Result.ok().count(page.getTotal()).data(fuzzyLogByPage).code(ResultCode.TABLE_SUCCESS);
    }
	// Return the exception log
    @Override
    public Result<ErrorLogDto> getFuzzyErrorLogByPage(Integer offectPosition, Integer limit, LogQuery logQuery) {
        Page page = PageHelper.offsetPage(offectPosition,limit);
        List<ErrorLogDto> fuzzyErrorLogByPage = logDao.getFuzzyErrorLogByPage(logQuery);
        return Result.ok().count(page.getTotal()).data(fuzzyErrorLogByPage).code(ResultCode.TABLE_SUCCESS);
    }
	// Save logs to the database
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void save(String userName, String browser, String ip, ProceedingJoinPoint joinPoint, MyLog log) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        com.codermy.myspringsecurityplus.log.aop.MyLog myLog = method.getAnnotation(com.codermy.myspringsecurityplus.log.aop.MyLog.class);
        // Method path
        String methodName = joinPoint.getTarget().getClass().getName()+"."+signature.getName()+"()";
        StringBuilder params = new StringBuilder("{");
        / / parameter values
        Object[] argValues = joinPoint.getArgs();
        // Parameter name
        String[] argNames = ((MethodSignature)joinPoint.getSignature()).getParameterNames();
        if(argValues ! =null) {for (int i = 0; i < argValues.length; i++) {
                params.append("").append(argNames[i]).append(":").append(argValues[i]); }}/ / description
        if(log ! =null) {
            log.setDescription(myLog.value());
        }
        assertlog ! =null;
        log.setIp(ip);
        String loginPath = "login";
        if(loginPath.equals(signature.getName())){
            try {
                assertargValues ! =null;
                userName = new JSONObject(argValues[0]).get("userName").toString();
            }catch (Exception e){
                e.printStackTrace();
            }
        }
        log.setMethod(methodName);
        log.setUserName(userName);
        log.setParams(params.toString() + "}");
        log.setBrowser(browser);
        logDao.save(log);
    }
	// Delete exception logs
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delAllByError(a) {
        logDao.delAllByInfo("ERROR");
    }
	// Delete user logs
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delAllByInfo(a) {
        logDao.delAllByInfo("INFO"); }}Copy the code

LogController

/ * * *@author codermy
 * @createTime2020/8/8 * /
@Controller
@RequestMapping("/api")
@API (tags = "System: Log Management ")
public class LogController {
    @Autowired
    private MyLogService logService;

    @GetMapping("/log/index")
    public String logIndex(a){
        return "system/log/log";
    }

    @GetMapping("/log")
    @ResponseBody
    @apiOperation (value = "log list ")
    @PreAuthorize("hasAnyAuthority('log:list')")
    public Result<LogDto> logList(PageTableRequest pageTableRequest, LogQuery logQuery){
        pageTableRequest.countOffset();
        logQuery.setLogType("INFO");
        return logService.getFuzzyInfoLogByPage(pageTableRequest.getOffset(),pageTableRequest.getLimit(),logQuery);
    }

    @DeleteMapping("/log")
    @mylog (" Delete all INFO logs ")
    @ResponseBody
    @apiOperation (" Delete all INFO logs ")
    @PreAuthorize("hasAnyAuthority('log:del')")
    public Result<Object> delAllByInfo(a){
        logService.delAllByInfo();
        return Result.ok().message("Deleted successfully");
    }

    @GetMapping("/log/error/index")
    public String errorLogIndex(a){
        return "system/log/errorLog";
    }

    @GetMapping("/error/log")
    @ResponseBody
    @apiOperation (value = "error log ")
    @PreAuthorize("hasAnyAuthority('errorLog:list')")
    public Result<ErrorLogDto> errorLogList(PageTableRequest pageTableRequest, LogQuery logQuery){
        pageTableRequest.countOffset();
        logQuery.setLogType("ERROR");
        return logService.getFuzzyErrorLogByPage(pageTableRequest.getOffset(),pageTableRequest.getLimit(),logQuery);
  }
    @DeleteMapping("/error/log")
    @mylog (" Delete all ERROR logs ")
    @ResponseBody
    @apiOperation (" Delete all ERROR logs ")
    @PreAuthorize("hasAnyAuthority('errorLog:del')")
    public Result<Object> delAllByError(a){
        logService.delAllByError();
        return Result.ok().message("Deleted successfully"); }}Copy the code

The corresponding front-end page will not be posted, if necessary, you can get it in my Gitee and Github

Nine, use

All we need to do is add @mylog annotations to the corresponding interface

We can test the collection of exceptions by making an exception ourselves

X. Start the test

Start the project and access the test normally. Logs will be collected automatically.

This seriesgiteeandgithubSynchronous update in