登录校验可以利用spring mvc的拦截器interceptor,实现handlerinterceptor接口即可。实现该接口后,会在把请求发给controller之前进行拦截处理。
拦截器的实现分为以下两个步骤:
创建⾃定义拦截器,实现 handlerinterceptor 接⼝的 prehandle(执⾏具体⽅法之前的预处理)⽅法。
将⾃定义拦截器加⼊ webmvcconfigurer 的 addinterceptors ⽅法中。
我们使用session来作为登录校验的例子,实现如下:
package com.demo;import lombok.extern.slf4j.slf4j;import org.springframework.stereotype.component;import org.springframework.web.servlet.handlerinterceptor;import javax.servlet.http.httpservletrequest;import javax.servlet.http.httpservletresponse;import javax.servlet.http.httpsession;/** * 登录拦截器 */@component@slf4jpublic class logininterceptor implements handlerinterceptor { /** * 为 false 则不能继续往下执行;为 true 则可以。 */ @override public boolean prehandle(httpservletrequest request, httpservletresponse response, object handler) throws exception { // 判断session的信息是否合法 httpsession session = request.getsession(false); if (session != null && session.getattribute("userinfo") != null) { return true; } log.error("当前用户没有访问权限"); response.setstatus(401); return false; }}
将写好的⾃定义拦截器通过webmvcconfigurer注册到容器中,使得拦截器生效,具体实现代码如下:
package com.demo;import org.springframework.beans.factory.annotation.autowired;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;@configurationpublic class myconfig implements webmvcconfigurer { @autowired private logininterceptor logininterceptor; @override public void addinterceptors(interceptorregistry registry) { registry.addinterceptor(logininterceptor) .addpathpatterns("/**") // 拦截所有请求 .excludepathpatterns("/user/login"); // 排除不拦截的 url }}
如果不注入对象的话,addinterceptor() 的参数也可以直接 new 一个对象:
@configuration // 一定不要忘记public class myconfig implements webmvcconfigurer { @override public void addinterceptors(interceptorregistry registry) { registry.addinterceptor(new logininterceptor()) .addpathpatterns("/**") // 拦截所有请求 .excludepathpatterns("/user/login"); // 排除不拦截的 url }}
原理
所有的 controller 执⾏都会通过spring mvc的调度器 dispatcherservlet 来实现,所有⽅法都会执⾏ dispatcherservlet 中的 dodispatch 调度⽅法,dodispatch 源码如下:
protected void dodispatch(httpservletrequest request, httpservletresponse response) throws exception { httpservletrequest processedrequest = request; handlerexecutionchain mappedhandler = null; boolean multipartrequestparsed = false; webasyncmanager asyncmanager = webasyncutils.getasyncmanager(request); try { try { modelandview mv = null; object dispatchexception = null; try { // ... 忽略不重要的代码 // 调⽤预处理 if (!mappedhandler.applyprehandle(processedrequest, respon se)) { return; } // 执⾏ controller 中的业务 mv = ha.handle(processedrequest, response, mappedhandler.g ethandler()); // ... 忽略不重要的代码 } catch (exception var20) { dispatchexception = var20; } catch (throwable var21) { dispatchexception = new nestedservletexception("handler di spatch failed", var21); } this.processdispatchresult(processedrequest, response, mappedh andler, mv, (exception)dispatchexception); } catch (exception var22) { this.triggeraftercompletion(processedrequest, response, mapped handler, var22); } catch (throwable var23) { this.triggeraftercompletion(processedrequest, response, mapped handler, new nestedservletexception("handler processing failed", var23)); } } finally { if (asyncmanager.isconcurrenthandlingstarted()) { if (mappedhandler != null) { mappedhandler.applyafterconcurrenthandlingstarted(processe drequest, response); } } else if (multipartrequestparsed) { this.cleanupmultipart(processedrequest); } }}
从上述源码可以看出在开始执⾏ controller 之前,会先调⽤ 预处理⽅法 applyprehandle,⽽ applyprehandle ⽅法的实现源码如下:
boolean applyprehandle(httpservletrequest request, httpservletresponse response) throws exception { for(int i = 0; i < this.interceptorlist.size(); this.interceptorindex = i++) { // 获取项⽬中使⽤的拦截器 handlerinterceptor handlerinterceptor interceptor = (handlerinterceptor)this.intercep torlist.get(i); if (!interceptor.prehandle(request, response, this.handler)) { this.triggeraftercompletion(request, response, (exception)null ); return false; } } return true;}
异常处理请求时的异常处理也是比较常见的统一处理的对象。
统⼀异常处理使⽤的是 @controlleradvice + @exceptionhandler 来实现的,@controlleradvice 表示控制器通知类,@exceptionhandler 是异常处理器,两个结合表示当出现异常的时候执⾏某个通知,也就是执⾏某个⽅法事件,具体实现代码如下:
package com.demo;import org.springframework.web.bind.annotation.controlleradvice;import org.springframework.web.bind.annotation.exceptionhandler;import org.springframework.web.bind.annotation.responsebody;import java.util.hashmap;/** * 统一处理异常 * 一般都需要自定义一个异常对象,这里为了简单说明只用一个map对象来说明 */@controlleradvicepublic class erroradive { @exceptionhandler(exception.class) @responsebody public hashmap<string, object> exceptionadvie(exception e) { hashmap<string, object> result = new hashmap<>(); result.put("code", "-1"); result.put("msg", e.getmessage()); return result; } @exceptionhandler(arithmeticexception.class) @responsebody public hashmap<string, object> arithmeticadvie(arithmeticexception e) { hashmap<string, object> result = new hashmap<>(); result.put("code", "-2"); result.put("msg", e.getmessage()); return result; }}
此时若出现异常就不会报错了,代码会继续执行,但是会把自定义的异常信息返回给前端!
原理@controlleradvice是spring的aop对于controller进行切面所有属性的,包括切入点和需要织入的切面逻辑,配合@exceptionhandler来捕获controller中抛出的指定类型的异常,从而达到不同类型的异常区别处理的目的。
返回数据结构统⼀的返回数据结构可以使用 @controlleradvice + responsebodyadvice接口 的方式实现,具体实现代码如下:
package com.demo;import org.springframework.core.methodparameter;import org.springframework.http.mediatype;import org.springframework.http.server.serverhttprequest;import org.springframework.http.server.serverhttpresponse;import org.springframework.web.bind.annotation.controlleradvice;import org.springframework.web.servlet.mvc.method.annotation.responsebodyadvice;import java.util.hashmap;/** * 统一返回数据的处理 */@controlleradvicepublic class responseadvice implements responsebodyadvice { /** * 内容是否需要重写(通过此⽅法可以选择性部分控制器和⽅法进⾏重写) * 返回 true 表示重写 */ @override public boolean supports(methodparameter returntype, class convertertype) { return true; } /** * ⽅法返回之前调⽤此⽅法 */ @override public object beforebodywrite(object body, methodparameter returntype, mediatype selectedcontenttype, class selectedconvertertype, serverhttpr equest request, serverhttpresponse response) { // 构造统⼀返回对象 hashmap<string, object> result = new hashmap<>(); result.put("state", 1); result.put("msg", ""); result.put("data", body); return result; }}
以上就是springboot统一处理功能如何实现的详细内容。
