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