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

来源:互联网 发布:淘宝卖家常用app 编辑:程序博客网 时间:2024/05/25 05:35

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

所需材料:
    注解: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;}@Overridepublic 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 {@Overridepublic 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 {@Resourceprivate 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

0 0