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当中。

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