基于注解和拦截器的权限控制

        互联网中各种各样的系统非常多,但是始终都离不开权限控制。一个好的权限控制对系统的安全性有着极大的提升,最近写了一个简单实用的权限验证的小例子,在这里分享下我的想法。
        我们使用注解+拦截器来实现权限控制,以注解来标示某个类或者方法的权限,用拦截器来验证用户的权限,把权限控制细化到了每个方法。

所需材料:
    注解:PrivCtrl
    控制器:UserController
    Bean:User,Request
    枚举:PrivCtrlType
    工具:RequestFactory,HttpClientUtil,SessionUtil
    拦截器:PrivCtrlInterceptor


首先我们需要一个枚举类来保存我们系统所拥有的各种权限类型:

package com.liyh.enumeration;

public enum PrivCtrlType {
	/** 游客 */
	PUBLIC(0),
	/** 会员 */
	MEMBER(1);
	
	private int code;
	
	private PrivCtrlType(int code) {
		this.code = code;
	}
	
	@Override
	public String toString() {
		return String.valueOf(code);
	}
}

紧接着我们需要定义一个注解用来标示类和方法的权限:

package com.liyh.annotation;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

import com.liyh.enumeration.PrivCtrlType;

@Retention(RetentionPolicy.RUNTIME)
public @interface PrivCtrl {
	PrivCtrlType[] value() default {PrivCtrlType.PUBLIC};
}

使用一个用户对象来记录用户信息,一个Session工具来操作当前会话的用户以及其他信息:

package com.liyh.domain;

import com.liyh.enumeration.PrivCtrlType;


public class User {

	private String id;
	private String username;
	private String password;
	private String type = PrivCtrlType.MEMBER.toString();

	public String getId() {
		return id;
	}

	public void setId(String id) {
		this.id = id;
	}

	public String getUsername() {
		return username;
	}

	public void setUsername(String username) {
		this.username = username;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	public String getType() {
		return type;
	}

	public void setType(String type) {
		this.type = type;
	}

}
package com.liyh.utils;

import java.util.Date;

import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

import com.liyh.domain.User;

public class SessionUtil {
	
	private static final String KEY_USER = "User";
	private static final String KEY_LOGIN_TIME = "loginTime";
	
	public static void invalid() {
		RequestAttributes requestAttributes =  RequestContextHolder.getRequestAttributes();
		if (requestAttributes != null) {
			requestAttributes.removeAttribute(KEY_USER, RequestAttributes.SCOPE_SESSION);
			requestAttributes.removeAttribute(KEY_LOGIN_TIME, RequestAttributes.SCOPE_SESSION);
		}
	}
	
	public static void login(User user) {
		RequestAttributes requestAttributes =  RequestContextHolder.getRequestAttributes();
		if (requestAttributes != null) {
			requestAttributes.setAttribute(KEY_USER, user, RequestAttributes.SCOPE_SESSION);
			requestAttributes.setAttribute(KEY_LOGIN_TIME, new Date(), RequestAttributes.SCOPE_SESSION);
		}
	}
	
	public static User getCurrentUser() {
		User user = null;
		RequestAttributes requestAttributes =  RequestContextHolder.getRequestAttributes();
		if (requestAttributes != null) {
			user = (User)requestAttributes.getAttribute(KEY_USER, RequestAttributes.SCOPE_SESSION);
		}
		return user;
	}
	
	public static String getUserId() {
		User user = getCurrentUser();
		if (user != null) {
			return user.getId();
		}
		return null;
	}
	
	public static Date getlastLoginTime() {
		Date lastLoginTime = null;
		RequestAttributes requestAttributes =  RequestContextHolder.getRequestAttributes();
		if (requestAttributes != null) {
			lastLoginTime = (Date) requestAttributes.getAttribute(KEY_LOGIN_TIME, RequestAttributes.SCOPE_SESSION);
		}
		return lastLoginTime;
	}
}

还需要一个对象来记录请求信息,一个工厂来实例化一个请求信息:

package com.liyh.domain.utils;

import java.util.Map;

/* 请求参数 */
public class Request {
	
	private boolean send;

	private String url;
	
	private String method;
	
	private Map<String, String[]> parameterMap;

	public boolean isSend() {
		return send;
	}

	public void setSend(boolean send) {
		this.send = send;
	}

	public String getUrl() {
		return url;
	}

	public void setUrl(String url) {
		this.url = url;
	}

	public String getMethod() {
		return method;
	}

	public void setMethod(String method) {
		this.method = method;
	}

	public Map<String, String[]> getParameterMap() {
		return parameterMap;
	}

	public void setParameterMap(Map<String, String[]> parameterMap) {
		this.parameterMap = parameterMap;
	}

}
package com.liyh.factory;

import javax.servlet.http.HttpServletRequest;

import com.liyh.domain.utils.Request;

public class RequestFactory {
	
	public static Request createReuqest(HttpServletRequest request) {
		Request myRequest = new Request();
		myRequest.setUrl(request.getRequestURL().toString());
		myRequest.setMethod(request.getMethod());
		myRequest.setParameterMap(request.getParameterMap());
		return myRequest;
	}
}


我们首先通过拦截器验证当前用户是否有请求权限:

package com.liyh.interceptor;

import java.io.IOException;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import com.liyh.annotation.PrivCtrl;
import com.liyh.domain.User;
import com.liyh.enumeration.PrivCtrlType;
import com.liyh.factory.RequestFactory;
import com.liyh.utils.SessionUtil;

/** 权限拦截器 */
public class PrivCtrlInterceptor extends HandlerInterceptorAdapter {
	
	@Override
	public boolean preHandle(HttpServletRequest request,
			HttpServletResponse response, Object handler) throws Exception {
		if (!(handler instanceof HandlerMethod)) {
			return true;
		}
		PrivCtrl privCtrl = getPrivCtrl((HandlerMethod) handler);
		if (isLoginRequired(privCtrl) && !verifyPrivCtrl(privCtrl)) {
			loginRedirect(request, response);
		}
		return true;
	}
	
	/** 获取权限注解 */
	private PrivCtrl getPrivCtrl(HandlerMethod method) {
		PrivCtrl ctrlAnnnotation = method.getMethodAnnotation(PrivCtrl.class);
		if (ctrlAnnnotation == null) {
			ctrlAnnnotation = method.getBeanType().getAnnotation(PrivCtrl.class);
		}
		return ctrlAnnnotation;
	}
	
	/** 判断是否需要登录 */
	private boolean isLoginRequired(PrivCtrl privCtrl) {
		if (privCtrl == null) {
			return false;
		}
		for (PrivCtrlType privCtrlType : privCtrl.value()) {
			if (privCtrlType != PrivCtrlType.PUBLIC) {
				return true;
			}
		}
		return false;
	}
	
	/** 权限验证 */
	private boolean verifyPrivCtrl(PrivCtrl privCtrl) {
		User user = SessionUtil.getCurrentUser();
		if (user == null) {
			return false;
		}
		for (PrivCtrlType privCtrlType : privCtrl.value()) {
			if (privCtrlType.toString().equals(user.getType())) {
				return true;
			}
		}
		return false;
	}
	
	/** 登录重定向 */
	private void loginRedirect(HttpServletRequest request, HttpServletResponse response) throws IOException {
		request.getSession().setAttribute("request", RequestFactory.createReuqest(request));
		response.setStatus(302);
		response.addHeader("Location", request.getContextPath() + "/user/login");
		response.flushBuffer();
	}
}

权限验证不通过时保存请求信息并跳转到登录页面进行登录:

package com.liyh.controller;

import java.util.Map;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;

import com.liyh.annotation.PrivCtrl;
import com.liyh.domain.User;
import com.liyh.domain.utils.Request;
import com.liyh.enumeration.PrivCtrlType;
import com.liyh.service.UserService;
import com.liyh.utils.HttpClientUtil;
import com.liyh.utils.SessionUtil;
import com.liyh.utils.StringUtil;

@Controller
@RequestMapping("/user")
public class UserController {
	
	@Resource
	private UserService userService;
	
	@RequestMapping(value="/login", method = RequestMethod.GET)
	public ModelAndView login(Map<String, Object> model,
			HttpServletRequest request) {
		HttpSession session = request.getSession();
		Request preRequest = (Request) session.getAttribute("request");
		if (preRequest != null) {
			if (preRequest.isSend()) {
				session.removeAttribute("request");
			} else {
				preRequest.setSend(true);
			}
		}
		return new ModelAndView("user/login");
	}
	
	@RequestMapping(value="/login", method = RequestMethod.POST)
	public String login(String username, String password, Map<String, Object> model,
			HttpServletRequest request, HttpServletResponse response) throws Exception{
		User user = userService.findByUsername(username);
		if (user == null || !StringUtil.equals(user.getPassword(), password)) {
			model.put("rs", "fail");
			model.put("msg", "登录失败");
			model.put("username", username);
			return "user/login";
		}
		SessionUtil.login(user);
		HttpSession session = request.getSession();
		Request preRequest = (Request) session.getAttribute("request");
		if (preRequest != null) {
			session.removeAttribute("request");
			user = (User) session.getAttribute("User");
			HttpClientUtil.send(response, preRequest);
			return null;
		}
		return "redirect:/";
	}
	
	@PrivCtrl(PrivCtrlType.MEMBER)
	@RequestMapping(value="/login_success", method = RequestMethod.GET)
	public ModelAndView login_success(Map<String, Object> model, HttpServletRequest request) {
		return new ModelAndView("user/login_success");
	}
	
}

登录过后通过自制的Http客户端工具跳转到保存的请求页面:

package com.liyh.utils;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Map;

import javax.servlet.http.HttpServletResponse;

import com.liyh.domain.utils.Request;

public class HttpClientUtil {
	
	public static void send(HttpServletResponse response, Request request) throws IOException {
	   response.setContentType("text/html");
	   PrintWriter out = response.getWriter();
	   out.println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">");
	   out.println("<HTML>");
	   out.println(" <HEAD><TITLE>sender</TITLE></HEAD>");
	   out.println(" <BODY>");
	   out.println("<form name=\"submitForm\" action=\"" + request.getUrl() + "\" method=\"" + request.getMethod() +"\">");
	   Map<String, String[]> params = request.getParameterMap();
	   for (String key : params.keySet()) {  
           String[] values = params.get(key);
           for (String value : values) {
        	   out.println("<input type=\"hidden\" name=\"" + key + "\" value=\"" + value + "\"/>");
           }
       }  
	   out.println("</from>");
	   out.println("<script>window.document.submitForm.submit();</script> ");
	   out.println(" </BODY>");
	   out.println("</HTML>");
	   out.flush();
	}
}

总结:
        存在问题:Ajax请求的接口没有做额外处理,暂时还没有完善
        解决方案:拦截器中判断是否是请求接口,如果是请求接口,则返回权限不足,需要封装Ajax请求方法,未登录时跳转到登录页面并记录当前URL

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值