1. 程式人生 > >【Spring MVC攔截器+logback日誌+自定義註解】實現使用者鑑權登陸和訪問日誌記錄

【Spring MVC攔截器+logback日誌+自定義註解】實現使用者鑑權登陸和訪問日誌記錄

摘要說明:

專案中經常這樣的需要 

1. 登陸鑑權:比如使用者瀏覽器發出某個請求的時候我們需要判斷這個使用者是否已經登陸,也就是cookie中是否有他的登陸資訊。

2. 訪問日誌記錄:使用者訪問請求的時候我們有必要記錄訪問者的身份資訊以及訪問了哪個url,請求引數是什麼,這個請求的耗時是多少等等。

我的專案中是通過如下方法實現的。先貼上程式碼,再逐步講解原理。

logback.xml的定義如下 【利用slf4j+logback配置日誌的最新日誌記錄方法請檢視這裡】
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="60 seconds" debug="false">

    <contextName>jweb_wb_mgmt</contextName>
    <property name="path" value="/data/jweblog/jweb_wb_mgmt_beta"/>

    <!-- 控制檯 -->
    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>[%date{HH:mm:ss.SSS}] [%thread] [%-5level] [%logger{36}] %msg%n</pattern>
        </encoder>
    </appender>

    <!-- 訪問日誌 -->
    <appender name="access" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>${path}/access/access.%d{yyyy-MM-dd}.log
            </fileNamePattern>
            <maxHistory>3</maxHistory>
        </rollingPolicy>
        <encoder>
            <pattern>[%date{HH:mm:ss.SSS}] [%thread] [%-5level] [%logger{36}] %msg%n</pattern>
        </encoder>
    </appender>

    <!-- 訪問日誌 -->
    <logger name="access" level="DEBUG" additivity="false">
        <appender-ref ref="access"/>
    </logger>

    <root level="INFO">
        <appender-ref ref="console"/>
    </root>
</configuration>

spring-mvc.xml定義攔截器如下   【springMVC攔截器的講解請檢視這裡】

<mvc:interceptors>
        <bean id="logInterceptor" class="com.zjr.common.web.interceptor.LogInterceptor"/>
        <bean id="agentAuthInterceptor" class="com.zjr.common.web.interceptor.AgentAuthInterceptor"/>
</mvc:interceptors>
LogInterceptor.java攔截器定義如下   【記錄訪問日誌】
package com.zjr.common.web.interceptor;


// import ....


public class LogInterceptor extends HandlerInterceptorAdapter {


	Logger logger = LoggerFactory.getLogger("access");


	long beginTime;


	/**
	 * Log the request entrance
	 */
	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {


		beginTime = System.currentTimeMillis();


		Map<String, Cookie> cookies = HttpUtils.getCookies(request.getCookies());
		String userId = String.valueOf(cookies.get("userId") != null ?
				cookies.get("userId").getValue() : request.getHeader("userId"));
		String token = String.valueOf(cookies.get("token") != null ?
				cookies.get("token").getValue() : request.getHeader("token"));
		String agentUserId = String.valueOf(cookies.get("agentUserId") != null ?
				cookies.get("agentUserId").getValue() : request.getHeader("agentUserId"));
		String agentUserToken = String.valueOf(cookies.get("agentUserToken") != null ?
				cookies.get("agentUserToken").getValue() : request.getHeader("agentUserToken"));


		//記錄請求的使用者資訊
		logger.info("Request URI:{} userId:{} token:{} agentUserId:{} agentUserToken:{}",
				request.getRequestURI(), userId, token, agentUserId, agentUserToken); 


		if (logger.isDebugEnabled()) {  
			StringBuilder sb = new StringBuilder();
			for (String key : request.getParameterMap().keySet()) {
				sb.append(key).append(":").append(request.getParameterValues(key)[0]).append(";");
			}
			//記錄請求引數
			logger.debug("Request URI:{} data:{}", request.getRequestURI(), sb);  
		}


		return true;
	}


	/**
	 * Log the request finished 記錄整個請求所花費的時間
	 */
	@Override
	public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
			ModelAndView modelAndView) throws Exception {
		logger.info("Request URI:{} Cost:{}", request.getRequestURI(), (System.currentTimeMillis() - beginTime) + "ms");
	}


	/**
	 * Log the request exit
	 */
	@Override
	public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
			throws Exception {
	}
}

AgentAuthCheck.java【自定義的註解@AgentAuthCheck】

package com.zjr.common.web.auth;

//import ...

/**
 * 代理登入鑑權註解
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AgentAuthCheck {

	/**
	 * 是否需要使用者鑑權
	 */
	boolean authRequired() default false;
}

AgentAuthInterceptor.java攔截器 【登陸鑑權】

package com.zjr.common.web.interceptor;

//import ...

public class AgentAuthInterceptor extends HandlerInterceptorAdapter {

	private static Logger logger = LoggerFactory.getLogger("access");

	@Autowired
	private Agent2UserBiz agent2UserBiz;

	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {

		// 使用者登入鑑權判斷
		if (handler instanceof HandlerMethod) {

			// 獲取鑑權設定
			HandlerMethod method = (HandlerMethod) handler; 
			AgentAuthCheck agentAnnotation = method.getMethodAnnotation(AgentAuthCheck.class);
			
			if (agentAnnotation != null && agentAnnotation.authRequired()) { 
				String agentUserId = BaseWebservice.getValueFromHeaderOrCookie(request, "agentUserId");
				agentUserId = agentUserId == null ? null : URLDecoder.decode(agentUserId, "utf-8");
				String agentUserToken = BaseWebservice.getValueFromHeaderOrCookie(request, "agentUserToken");

				Agent2User user = agent2UserBiz.checkLogin(agentUserId, agentUserToken);

				if (user != null) {

					AgentThreadData.setAgent2User(user);

					logger.info("AuthInterceptor checkLogin success. URI:{} agentUserId:{} agentUserToken:{}",
							request.getRequestURI(), agentUserId, agentUserToken);
				} else {
					BaseWebservice.setCookie(response, "agentId", "", 0);
					BaseWebservice.setCookie(response, "agentUserId", "", 0);
					BaseWebservice.setCookie(response, "agentUserName", "", 0);
					BaseWebservice.setCookie(response, "agentUserToken", "", 0);
					BaseWebservice.responseJson(response, BasicResult.createFailResult(ErrorType.LOGIN_AUTH_ERROR), 200);

					logger.warn("AuthInterceptor checkLogin fail. URI:{} agentUserId:{} agentUserToken:{}",
							request.getRequestURI(), agentUserId, agentUserToken);

					return false;
				}
			}
		}

		return true;

	}

	@Override
	public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
			ModelAndView modelAndView) throws Exception {
	} 

	@Override
	public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
			throws Exception {
		AgentThreadData.removeAgent2User();
	}


}

在訪問請求出新增自定義註解實現鑑權
package com.zjr.modules.agent2.webservice;

//import ...

@RestController
@RequestMapping(value = {"/agent2/user"})
public class Agent2ShopService {

	//登陸不需要鑑權
    @RequestMapping(value = "/login")
    public BasicResult login(@RequestParam ...) {

        //使用者登入的實現程式碼
		
    }

	//新增使用者請求需要鑑權
    @AgentAuthCheck(authRequired = true)
    @RequestMapping(value = "/addUser")
    public BasicResult addUser(@RequestParam ...) {

        //新增使用者的實現程式碼
		
    }
}

當前端發出/agent2/user/add的請求的時候,攔截器LogInterceptor和AgentAuthInterceptor會依次攔截請求執行自己的操作,LogInterceptor攔截器會用日誌記錄使用者的資訊以及請求的引數,返回true,交給AgentAuthInterceptor攔截器,AgentAuthInterceptor攔截器會判斷響應請求的方法是否有@AgentAuthCheck註解同時authRequired屬性是否為true,如果都有,那麼執行鑑權操作,鑑權成功則返回true,響應請求的方法addUser執行,鑑權失敗則返回false,請求不繼續執行。