一般情况下请求的资源分三种:1.不需要登录就可以访问,2.登录后才能访问,3.登录后并且需要权限才能访问
本例中自定义的注解都是放在controller的方法上的,
@GreenPath对应第一种
@Auth对应第三种
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * @description 权限验证的注解类 * * @Inherited:在使用此自定义注解时,如果注解在类上面时,子类会自动继承此注解,否则,子类不会继承此注解。这里一定要记住,使用Inherited声明出来的注解,只有在类上使用时才会有效,对方法,属性等其他无效。 * @Target:表示此注解可以放置的位置。常见的位置有:TYPE=枚举或注解上,FIELD=字段上,METHOD=方法上,PARAMETER=函数形参列表中,CONSTRUCTOR=构造函数上,LOCAL_VARIABLE=局部变量上 等等其他位置。 * @Retention:此注解的生命周期。常见的有:SOURCE=源码时期;CLASS=字节码时期(已编译);RUNTIME=运行时期,通常是用这个的时候要多。 * @Documentd:生成注解文档。 */ @Inherited @Target(ElementType.METHOD) @Documented @Retention(RetentionPolicy.RUNTIME) public @interface Auth { // 使用spring的@AliasFor别名 String[] value() default "";// 权限标识 boolean auth() default true;// 默认为鉴权 }
import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Inherited @Target(ElementType.METHOD) @Documented @Retention(RetentionPolicy.RUNTIME) public @interface GreenPath { }
在controller中使用
@RequestMapping("/emailList") @Auth(value={"email:emailList"}) public String emailList(HttpServletRequest request){ return "emailList" ; } /** * 跳转到登录页面 * @return */ @GreenPath @RequestMapping(value={"/index","/",""}) public String indexPage(HttpSession session){ return "login" ; }
拦截器
import java.util.List; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.web.method.HandlerMethod; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import com.meihaocloud.manage.common.utils.EHcache; import com.meihaocloud.manage.common.utils.IPUtils; import com.meihaocloud.manage.common.utils.WebUtil; import com.meihaocloud.manage.common.vo.Auth; import com.meihaocloud.manage.common.vo.Constant; import com.meihaocloud.manage.common.vo.ErrorEnum; import com.meihaocloud.manage.common.vo.GreenPath; import com.meihaocloud.manage.common.vo.Result; import com.meihaocloud.manage.system.domain.SysUser; import com.meihaocloud.manage.system.service.SysUserService; /** * @author 008 * @Date 2019年1月21日 下午10:41:38 * @Desc springmvc 拦截器,在MVCConfig 里面配置 */ @Component public class AuthInterruptor implements HandlerInterceptor { private static final Log log = LogFactory.getLog(AuthInterruptor.class) ; @Autowired private SysUserService userService ; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { /** * 在此可以统计一些请求信息,referer,userAgent , ip等 */ String uri = request.getRequestURI(); String referer = request.getHeader("Referer") == null ? request.getHeader("referer") : request.getHeader("Referer"); String ip = IPUtils.getClientIP(request); String userAgent = request.getHeader("User-Agent") == null ? request.getHeader("user-agent") : request.getHeader("User-Agent"); StringBuffer buf = new StringBuffer() ; buf.append("uri=").append(uri).append(" | ") .append("ip=").append(ip).append(" | ") .append("referer=").append(referer).append(" | ") .append("userAgent=").append(userAgent) ; log.info("request-info : "+ buf.toString()); if(uri.endsWith("/error")){ request.getRequestDispatcher("/404").forward(request, response) ; return false ; } // 如果不是映射到方法直接通过 if (!(handler instanceof HandlerMethod)) { return true; } HandlerMethod handlerMethod = (HandlerMethod) handler; GreenPath greenPath = handlerMethod.getMethodAnnotation(GreenPath.class); if(greenPath != null) { // GreenPath , 不需要登录 return true ; } HttpSession session = request.getSession(); SysUser user = (SysUser) session.getAttribute(Constant.sysUserName); if(user == null ){ //没有session,用户没有登录 if(WebUtil.isAjaxRequest(request)){//ajax请求 log.info(" isAjaxRequest ... "); Result result = new Result(ErrorEnum.SESSION_TIMEOUT.getCode(),ErrorEnum.SESSION_TIMEOUT.getMsg()); WebUtil.writeMsg(response, result); } else { log.info(" getRequestDispatcher ... "); request.getRequestDispatcher("/manage").forward(request, response); } return false ; } // 经过研究发现,只有当GET请求是请求静态文件时(在spring配置文件里会配置静态文件的URI), // handler的实际类型会是DefaultServletHttpRequestHandler,此时强制转换就会报错。 Auth auth = handlerMethod.getMethodAnnotation(Auth.class); if (auth != null && auth.auth()) {// 进行权限校验 // 对当前访问的权限进行校验 boolean b = authPermission(request, auth); if (!b) { log.info("无权访问资源 。。。"); if(WebUtil.isAjaxRequest(request)){//ajax请求 log.info(" isAjaxRequest ... "); Result result = new Result(ErrorEnum.NO_PERMISSION.getCode(),ErrorEnum.NO_PERMISSION.getMsg()); WebUtil.writeMsg(response, result); } else { log.info("getRequestDispatcher ... "); request.getRequestDispatcher("/403").forward(request, response); } return false; } } return true; } @SuppressWarnings("unchecked") private boolean authPermission(HttpServletRequest request, Auth auth) { String[] action = auth.value(); if(action.length == 0){ //加了@Auth ,但没有权限标识 , 返回false,鉴权失败 log.info("加了@Auth,但没有权限标识符 , 可能写漏了 " + request.getRequestURI()); return false ; } List<String> permission = null ; //获取所有权限 Object obj = EHcache.get(Constant.permission); if(obj == null) { //获取所有权限 , 保存到EHCache // log.info("查询权限。。。"); SysUser user = (SysUser) request.getSession().getAttribute(Constant.sysUserName); permission = userService.findAllPermission(user.getUserId()); EHcache.put(Constant.permission, permission); }else { permission = (List<String>)obj ; } // log.info(Arrays.toString(action) + " , permission : " + permission); for (int i = 0; i < action.length; i++) { if(!permission.contains(action[i])){ // 有多个权限标识符时, 只有用户没有其中一个权限,鉴权失败,返回false return false ; } } return true; } /** * 该方法将在Controller执行之后,返回视图之前执行,modelAndView表示请求Controller处理之后返回的Model和View对象,所以可以在 * 这个方法中修改modelAndView的属性,从而达到改变返回的模型和视图的效果。 */ @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { } }
拦截器配置
import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import com.meihaocloud.manage.common.Auth.AuthInterruptor; /** * jdk8使用 implements WebMvcConfigurer, 接口可以有default方法 * jdk8一下使用extends WebMvcConfigurerAdapter */ @Configuration public class MVCConfig implements WebMvcConfigurer { //extends WebMvcConfigurerAdapter @Override public void addInterceptors(InterceptorRegistry registry) { registry .addInterceptor(new AuthInterruptor()) .addPathPatterns("/**") .excludePathPatterns("/static/**") .excludePathPatterns("/css/**") .excludePathPatterns("/fonts/**") .excludePathPatterns("/img/**") .excludePathPatterns("/js/**") .excludePathPatterns("/layui/**") .excludePathPatterns("/rsa/**") .excludePathPatterns("/treeview/**") .excludePathPatterns("/wangEditor/**") .excludePathPatterns("/ueditor/**") ;//登录页 } /** * 配置静态资源 */ @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/"); registry.addResourceHandler("/templates/**").addResourceLocations("classpath:/templates/"); } }