java項目中關於登錄攔截器的實現 (涉及註解的創建與使用,springboot的攔截器的創建與註冊,session的校驗)

本文主要寫了一個關於登錄校驗的demo,使用範圍,java 項目進行接口請求的校驗,採用了springboot框架+註解+攔截器的方式來實現,也是當前行業中比較常用的一種模式,現在主要對代碼流程進行解析和說明。

創建一個註解

@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Auth {
}

所謂註解就是爲類或方法加上一個標識,沒有任何作用。擴展一下:

jdk1.5之後Java增加了對元數據(MetaData)的支持,也就是Annotation(註解),他是代碼裏的特殊標記,
這些標記可以在編譯,類加載,運行時被讀取,並執行相應操作。通過使用註解可以在不改變原有邏輯的情況下,
在源文件中添加補充信息,代碼分析工具,開發工具,部署工具,可以更具這些信息進行驗證和部署。
Annotation就像Java修飾符一樣,可以用於修飾包、類、構造器、方法、變量、參數、局部變量,這些信息
存儲在Annotation的"name=value“中。
Annotation對程序的運行無影響,如果希望Annotation在運行時其到作用,就需要通過配套工具對
annotation中的信息進行訪問處理,這一工具統稱爲APT。

這裏我們創建一個最簡單的註解,精確到方法上,也可以作用在類上。

創建攔截器


@Component
public class LoginInterceptor extends HandlerInterceptorAdapter {
	@Autowired
	protected AdminSecurityManager adminSecurityManager;

	public LoginInterceptor(AdminSecurityManager adminSecurityManager) {
		this.adminSecurityManager = adminSecurityManager;
	}

	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		adminSecurityManager.setSession(request.getSession());
		if (!handler.getClass().isAssignableFrom(HandlerMethod.class)) {
			return true;
		}
		UserExt user = JSONObject.toJavaObject(adminSecurityManager.getPrincipal(), UserExt.class);

		final HandlerMethod handlerMethod = (HandlerMethod) handler;
		final Method method = handlerMethod.getMethod();
		final Class<?> clazz = method.getDeclaringClass();
		if (clazz.isAnnotationPresent(Auth.class) || method.isAnnotationPresent(Auth.class)) {
			if (user == null) {
				JSONObject res = new JSONObject();
				res.put("code", 401);
				res.put("msg", "You did not login.");
				res.put("data", "");
				PrintWriter out = response.getWriter();
				out.append(res.toString());
				return false;
			}
			if (CommonConstants.VALUE_YES.equals(user.getDeletedFlag())
					|| !CommonConstants.STATUS_ACTIVE.equals(user.getStatus())
					|| !adminSecurityManager.hasPermission(request, adminSecurityManager.getPrincipal())) {
				JSONObject res = new JSONObject();
				res.put("code", 403);
				res.put("msg", " to access");
				res.put("data", "");
				PrintWriter out = response.getWriter();
				out.append(res.toString());
				return false;
			}
			// 設置登陸用戶
				request.setAttribute("currentUser", user);
		}
		return true;

	}
	}

@Component
public class AdminSecurityManager extends SecurityManager<JSONObject> {

    private String key = "bog";

    @Override
    public String getKey() {
        return key;
    }

    @Override
	public boolean hasPermission(HttpServletRequest request,JSONObject admin) {
	    if (admin == null){
            return false;
        }
        try {
            String requestURI = WebUtils.getCleanedURI(request.getRequestURI());
            // Check if the requestURI is in permission list
            String allPermissionUrls = (String) request.getSession().getAttribute(CommonConstants.ALL_PERMISSIONS);
            if (allPermissionUrls.contains(requestURI + ";")) {
                JSONArray permissions = admin.getJSONArray("permissionUrlList");
                return permissions.stream().anyMatch(n -> Pattern.compile((String) n).matcher(requestURI).matches());
            } else {
            	return true;
            }
        }catch (Exception e){
            e.printStackTrace();
            return false;
        }


	}

    @Override
    public Number principalId() {
        return null;
    }


    public Admin getAdmin() {
        JSONObject principal = super.getPrincipal();
        return JSONObject.toJavaObject(principal, Admin.class);
    }

}


public abstract class SecurityManager<T> {

	protected ThreadLocal<HttpSession> session = new ThreadLocal<HttpSession>();

	public abstract String getKey();

	public void loginSession(T principal) {

		session.get().setAttribute(CommonConstants.REDIS_KEY + getKey(), principal);
	}

	public void setSession(HttpSession session) {
		this.session.set(session);
	}

	public void logout() {

		this.session.get().removeAttribute(CommonConstants.REDIS_KEY + getKey());

	}

	public abstract boolean hasPermission(HttpServletRequest request, T principal);

	public int hasVerify(HttpServletRequest request) {
		return 0;
	}

	public abstract Number principalId();

	@SuppressWarnings("unchecked")
	public boolean hasPermission(HttpServletRequest request) {
		T principal = getPrincipal();

		if (principal == null) {
			return false;
		}

		return hasPermission(request, principal);
	}

	@SuppressWarnings("unchecked")
	public T getPrincipal() {
		return (T) session.get().getAttribute(CommonConstants.REDIS_KEY + getKey());
	}

	public boolean setPrincipal(T principal) {
		session.get().setAttribute(CommonConstants.REDIS_KEY + getKey(), principal);
		return true;
	}

	public boolean getVerifyState(String url, int verifyType) {
		Integer verifyState = (Integer) session.get().getAttribute("verify_" + verifyType + ":" + url);

		if (verifyState != null) {
			session.get().removeAttribute(CommonConstants.REDIS_KEY + getKey());
			return false;
		}
		return true;
	}

	public void setVerifyState(String url, int verifyType) {
		session.get().setAttribute("verify_" + verifyType + ":" + url, verifyType);
	}

}

這裏的攔截器主要是集成了spring的HandlerAdapter 重寫了preHandler方法
preHandler方法的實現:

  • 首先重新設置當前操作的session信息
  • 判斷是否通過controler的方法進入(@RequestMapping)如果否直接放行。
  • 從session中獲取用戶信息將json字符串轉成對應的對象
  • 如果有@author註解,那麼進行用戶身份校驗,如果第三步取得的信息爲空則返回用戶登錄失敗信息。
  • 如果沒有@author註解,直接放行。
  • 最後如果登錄成功,可以將用戶信息放置於request中,我們可以在controller中的方法入參HttpServletRequest對象中獲取用戶信息。進而直接在邏輯中直接通過request.getAttrubute("")獲取

AdminSecurityManager

這個類主要是用來操作用戶信息,這裏是通過ThreadLocal來進行存儲用戶信息。通過redis去管理信息。

註冊攔截器


@Configuration
@EnableConfigurationProperties({ApiTsgProperties.class})
public class WebConfig implements WebMvcConfigurer {

	@Autowired
	protected AdminSecurityManager adminSecurityManager;

	@Bean
	public PermissionInterceptor permissionInterceptor() {
			return new PermissionInterceptor();
	}

    @Override
    public void addInterceptors(InterceptorRegistry registry){
        registry.addInterceptor(new LoginInterceptor(adminSecurityManager));
        registry.addInterceptor(permissionInterceptor());
    }
    
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**");
    }

}

springboot中所有自定義攔截器都需要將其註冊到webMvc適配器中。否則不會發生作用。

api添加註解

在這裏插入圖片描述

測試

測試發現,不添加Author註解,我們可以直接調用方法,如果增加了@author註解,就需要登錄後調用。

擴展

用戶登錄成功時候會將用戶信息存放在threadlocal當中。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章