干系:
spring boot + mybatis + layui + shiro后台权限管理系统
springboot + shiro之登录人数限定、登录判断重定向、session韶光设置
springboot + shiro 动态更新用户信息
基于前篇,新增功能:
新增shiro权限表明;要求乱码问题办理;统一非常处理。源码已集成到项目中:
github源码: https://github.com/wyait/manage.git
码云:https://gitee.com/wyait/manage.git
github对应项目源码目录:wyait-manage-1.2.0
码云对应项目源码目录:wyait-manage-1.2.0
shiro表明的利用shiro权限表明
Shiro 供应了相应的表明用于权限掌握,如果利用这些表明就须要利用AOP 的功能来进行判断,如Spring AOP;Shiro 供应了Spring AOP 集成用于权限表明的解析和验证。
@RequiresAuthentication 表示当前Subject已经通过login 进行了身份验证;即Subject.isAuthenticated()返回true。 @RequiresUser 表示当前Subject已经身份验证或者通过记住我登录的。 @RequiresGuest 表示当前Subject没有身份验证或通过记住我登录过,即是游客身份。 @RequiresRoles(value={“admin”, “user”}, logical= Logical.AND) @RequiresRoles(value={“admin”}) @RequiresRoles({“admin“}) 表示当前Subject须要角色admin 和user。 @RequiresPermissions (value={“user:a”, “user:b”}, logical= Logical.OR) 表示当前Subject须要权限user:a或user:b。
Shiro的认证表明处理是有内定的处理顺序的,如果有多个表明的话,前面的通过了会连续检讨后面的,若不通过则直接返回,处理顺序依次为(与实际声明顺序无关):
RequiresRolesRequiresPermissionsRequiresAuthenticationRequiresUserRequiresGuest
以上表明既可以用在controller中,也可以用在service中利用;
建议将shiro表明放在controller中,由于如果service层利用了spring的事物表明,那么shiro表明将无效。
shiro权限表明springAOP配置shiro权限表明要生效,必须配置springAOP通过设置shiro的SecurityManager进行权限验证。
/ @描述:开启Shiro的表明(如@RequiresRoles,@RequiresPermissions),需借助SpringAOP扫描利用Shiro表明的类,并在必要时进行安全逻辑验证 配置以下两个bean(DefaultAdvisorAutoProxyCreator和AuthorizationAttributeSourceAdvisor)即可实现此功能 </br>Enable Shiro Annotations for Spring-configured beans. Only run after the lifecycleBeanProcessor(担保实现了Shiro内部lifecycle函数的bean实行) has run </br>不该用表明的话,可以注释掉这两个配置 @创建人:wyait @创建韶光:2018年5月21日 下午6:07:56 @return / @Bean public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() { DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator(); advisorAutoProxyCreator.setProxyTargetClass(true); return advisorAutoProxyCreator; } @Bean public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() { AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor(); authorizationAttributeSourceAdvisor.setSecurityManager(securityManager()); return authorizationAttributeSourceAdvisor; }springboot非常处理事理
场景:当用户正常访问网站时,由于某种缘故原由后端涌现exception的时候,直接暴露非常信息或页面显示给用户;
这种操作体验不是我们想要的。以是要对非常进行统一管理,能提高用户体验的同时,后台能详细定位到非常的问题点。
springboot非常概况
Spring Boot供应了默认的统一缺点页面,这是Spring MVC没有供应的。在理解了Spring Boot供应的缺点处理干系内容之后,我们可以方便的定义自己的缺点返回的格式和内容。
编写by zero非常
在home页面,手动创建两个非常:普通非常和异步非常!
<p> 普通要求非常: <a href=\公众/error/getError\"大众>点击</a></p><p> ajax异步要求非常: <a href=\"大众javascript:void(0)\"大众 onclick=\"大众ajaxError()\"大众>点击</a></p>... //js代码function ajaxError(){ $.get(\公众/error/ajaxError\"大众,function(data){ layer.alert(data); });}后端代码:
/ @描述:普通要求非常 @创建人:wyait @创建韶光:2018年5月24日 下午5:30:50 /@RequestMapping(\"大众getError\公众)public void toError(){ System.out.println(1/0);}/ @描述:异步非常 @创建人:wyait @创建韶光:2018年5月24日 下午5:30:39 /@RequestMapping(\公众ajaxError\"大众)@ResponseBodypublic String ajaxError(){ System.out.println(1/0); return \"大众异步要求成功!
\公众;}
非常效果
普通非常:console缺点信息:
[2018-05-25 09:30:04.669][http-nio-8077-exec-8][ERROR][org.apache.juli.logging.DirectJDKLog][181]:Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.ArithmeticException: / by zero] with root causejava.lang.ArithmeticException: / by zero at com.wyait.manage.web.error.IndexErrorController.toError(IndexErrorController.java:18) ~[classes/:?] ... at java.lang.Thread.run(Thread.java:748) [?:1.8.0_131]...[2018-05-25 09:30:04.676][http-nio-8077-exec-8][DEBUG][org.springframework.web.servlet.handler.AbstractHandlerMethodMapping][317]:Returning handler method [public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)][2018-05-25 09:30:04.676][http-nio-8077-exec-8][DEBUG][org.springframework.web.servlet.handler.AbstractHandlerMethodMapping][317]:Returning handler method [public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)][2018-05-25 09:30:04.676][http-nio-8077-exec-8][DEBUG][org.springframework.beans.factory.support.AbstractBeanFactory][251]:Returning cached instance of singleton bean 'basicErrorController'[2018-05-25 09:30:04.676][http-nio-8077-exec-8][DEBUG][org.springframework.beans.factory.support.AbstractBeanFactory][251]:Returning cached instance of singleton bean 'basicErrorController'...[2018-05-25 09:30:04.686][http-nio-8077-exec-8][DEBUG][org.springframework.web.servlet.view.ContentNegotiatingViewResolver][263]:Requested media types are [text/html, text/html;q=0.8] based on Accept header types and producible media types [text/html])[2018-05-25 09:30:04.686][http-nio-8077-exec-8][DEBUG][org.springframework.web.servlet.view.ContentNegotiatingViewResolver][263]:Requested media types are [text/html, text/html;q=0.8] based on Accept header types and producible media types [text/html])[2018-05-25 09:30:04.686][http-nio-8077-exec-8][DEBUG][org.springframework.beans.factory.support.AbstractBeanFactory][251]:Returning cached instance of singleton bean 'error'[2018-05-25 09:30:04.686][http-nio-8077-exec-8][DEBUG][org.springframework.beans.factory.support.AbstractBeanFactory][251]:Returning cached instance of singleton bean 'error'[2018-05-25 09:30:04.686][http-nio-8077-exec-8][DEBUG][org.springframework.web.servlet.view.ContentNegotiatingViewResolver][338]:Returning [org.springframework.boot.autoconfigure.web.ErrorMvcAutoConfiguration$SpelView@6ffd99fb] based on requested media type 'text/html'[2018-05-25 09:30:04.686][http-nio-8077-exec-8][DEBUG][org.springframework.web.servlet.view.ContentNegotiatingViewResolver][338]:Returning [org.springframework.boot.autoconfigure.web.ErrorMvcAutoConfiguration$SpelView@6ffd99fb] based on requested media type 'text/html'...
通过日志可知,springboot返回的缺点页面,是通过:org.springframework.boot.autoconfigure.web.BasicErrorController.errorHtml处理返回ModelAndView。
异步非常:console日志信息:[2018-05-25 09:31:19.958][http-nio-8077-exec-6][ERROR][org.apache.juli.logging.DirectJDKLog][181]:Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.ArithmeticException: / by zero] with root causejava.lang.ArithmeticException: / by zero at com.wyait.manage.web.error.IndexErrorController.ajaxError(IndexErrorController.java:29) ~[classes/:?] ... at java.lang.Thread.run(Thread.java:748) [?:1.8.0_131]...[2018-05-25 09:31:19.960][http-nio-8077-exec-6][DEBUG][org.springframework.web.servlet.handler.AbstractHandlerMethodMapping][317]:Returning handler method [public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.BasicErrorController.error(javax.servlet.http.HttpServletRequest)][2018-05-25 09:31:19.960][http-nio-8077-exec-6][DEBUG][org.springframework.web.servlet.handler.AbstractHandlerMethodMapping][317]:Returning handler method [public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.BasicErrorController.error(javax.servlet.http.HttpServletRequest)][2018-05-25 09:31:19.960][http-nio-8077-exec-6][DEBUG][org.springframework.beans.factory.support.AbstractBeanFactory][251]:Returning cached instance of singleton bean 'basicErrorController'[2018-05-25 09:31:19.960][http-nio-8077-exec-6][DEBUG][org.springframework.beans.factory.support.AbstractBeanFactory][251]:Returning cached instance of singleton bean 'basicErrorController'...[2018-05-25 09:31:19.961][http-nio-8077-exec-6][DEBUG][org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor][234]:Written [{timestamp=Fri May 25 09:31:19 CST 2018, status=500, error=Internal Server Error, exception=java.lang.ArithmeticException, message=/ by zero, path=/error/ajaxError}] as \"大众application/json\公众 using [org.springframework.http.converter.json.MappingJackson2HttpMessageConverter@2729eae5][2018-05-25 09:31:19.961][http-nio-8077-exec-6][DEBUG][org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor][234]:Written [{timestamp=Fri May 25 09:31:19 CST 2018, status=500, error=Internal Server Error, exception=java.lang.ArithmeticException, message=/ by zero, path=/error/ajaxError}] as \"大众application/json\"大众 using [org.springframework.http.converter.json.MappingJackson2HttpMessageConverter@2729eae5][2018-05-25 09:31:19.961][http-nio-8077-exec-6][DEBUG][org.springframework.web.servlet.DispatcherServlet][1048]:Null ModelAndView returned to DispatcherServlet with name 'dispatcherServlet': assuming HandlerAdapter completed request handling...
通过日志可知,springboot返回的缺点信息,是通过:org.springframework.boot.autoconfigure.web.BasicErrorController.error处理返回ResponseEntity<String,Object>。
非常都是通过org.springframework.boot.autoconfigure.web.BasicErrorController掌握处理的。springboot非常处理解析
查看org.springframework.boot.autoconfigure.web包下面的类,跟踪springboot对error非常处理机制。自动配置通过一个MVC error掌握器处理缺点
通过spring-boot-autoconfigure引入
查看springboot 处理error的类
springboot的自动配置,在web中处理error干系的自动配置类:ErrorMvcAutoConfiguration。查看与处理error干系的类:
ErrorMvcAutoConfiguration.classErrorAttibutes.classErrorController.classErrorProperties.classErrorViewResolver.class...ErrorAutoConfiguration类源码//TODO
ErrorAutoConfiguration注册的bean
//4个BEAN@Bean@ConditionalOnMissingBean(value = ErrorAttributes.class, search = SearchStrategy.CURRENT)public DefaultErrorAttributes errorAttributes() { return new DefaultErrorAttributes();}@Bean@ConditionalOnMissingBean(value = ErrorController.class, search = SearchStrategy.CURRENT)public BasicErrorController basicErrorController(ErrorAttributes errorAttributes) { return new BasicErrorController(errorAttributes, this.serverProperties.getError(), this.errorViewResolvers);}@Beanpublic ErrorPageCustomizer errorPageCustomizer() { return new ErrorPageCustomizer(this.serverProperties);}@Beanpublic static PreserveErrorControllerTargetClassPostProcessor preserveErrorControllerTargetClassPostProcessor() { return new PreserveErrorControllerTargetClassPostProcessor();}DefaultErrorAttributes类
@Order(Ordered.HIGHEST_PRECEDENCE)public class DefaultErrorAttributes implements ErrorAttributes, HandlerExceptionResolver, Ordered { ... }
ErrorAttributes:
public interface ErrorAttributes { Map<String, Object> getErrorAttributes(RequestAttributes requestAttributes, boolean includeStackTrace); Throwable getError(RequestAttributes requestAttributes);}
HandlerExceptionResolver:
public interface HandlerExceptionResolver { / Try to resolve the given exception that got thrown during handler execution, returning a {@link ModelAndView} that represents a specific error page if appropriate. / ModelAndView resolveException( HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex);}
DefaultErrorAttributes类:
实现了ErrorAttributes接口,当处理/error缺点页面时,可以在该bean中读取缺点信息相应返回;实现了HandlerExceptionResolver接口。debug跟踪源码:即DispatcherServlet在doDispatch过程中有非常抛出时:
一. 先由HandlerExceptionResolver.resolveException解析非常并保存在request中;
二. 再DefaultErrorAttributes.getErrorAttributes处理;DefaultErrorAttributes在处理过程中,从request中获取缺点信息,将缺点信息保存到RequestAttributes中;
三. 末了在获取缺点信息getError(RequestAttributes)时,从RequestAttributes中取到缺点信息。
BasicErrorController类@Controller@RequestMapping(\"大众${server.error.path:${error.path:/error}}\公众)public class BasicErrorController extends AbstractErrorController { private final ErrorProperties errorProperties; ... @RequestMapping(produces = \"大众text/html\"大众) public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) { HttpStatus status = getStatus(request); Map<String, Object> model = Collections.unmodifiableMap(getErrorAttributes( request, isIncludeStackTrace(request, MediaType.TEXT_HTML))); response.setStatus(status.value()); ModelAndView modelAndView = resolveErrorView(request, response, status, model); return (modelAndView == null ? new ModelAndView(\"大众error\公众, model) : modelAndView); } @RequestMapping @ResponseBody public ResponseEntity<Map<String, Object>> error(HttpServletRequest request) { Map<String, Object> body = getErrorAttributes(request, isIncludeStackTrace(request, MediaType.ALL)); HttpStatus status = getStatus(request); return new ResponseEntity<Map<String, Object>>(body, status); } ...}
resolveErrorView方法(查找=error/“缺点状态码”;的资源):
如果不是非常要求,会实行resolveErrorView方法;该方法会先在默认或配置的静态资源路径下查找error/HttpStatus(缺点状态码)的资源文件,如果没有;利用默认的error页面。
public class DefaultErrorViewResolver implements ErrorViewResolver, Ordered { ... @Override public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status, Map<String, Object> model) { //status:非常缺点状态码 ModelAndView modelAndView = resolve(String.valueOf(status), model); if (modelAndView == null && SERIES_VIEWS.containsKey(status.series())) { modelAndView = resolve(SERIES_VIEWS.get(status.series()), model); } return modelAndView; } private ModelAndView resolve(String viewName, Map<String, Object> model) { //视图名称,默认是error/+“status”缺点状态码;比如:error/500、error/404 String errorViewName = \"大众error/\"大众 + viewName; TemplateAvailabilityProvider provider = this.templateAvailabilityProviders .getProvider(errorViewName, this.applicationContext); if (provider != null) { return new ModelAndView(errorViewName, model); } return resolveResource(errorViewName, model); } //在资源文件中查找error/500或error/404等页面 private ModelAndView resolveResource(String viewName, Map<String, Object> model) { for (String location : this.resourceProperties.getStaticLocations()) { try { Resource resource = this.applicationContext.getResource(location); resource = resource.createRelative(viewName + \"大众.html\"大众); if (resource.exists()) { return new ModelAndView(new HtmlResourceView(resource), model); } } catch (Exception ex) { } } return null; } ...}
BasicErrorController根据Accept头的内容,输出不同格式的缺点相应。比如针对浏览器的要求天生html页面,针对其它要求天生json格式的返回。
可以通过配置error/HttpStatus页面实现自定义缺点页面。
ErrorPageCustomizer类/ {@link EmbeddedServletContainerCustomizer} that configures the container's error pages. /private static class ErrorPageCustomizer implements ErrorPageRegistrar, Ordered { private final ServerProperties properties; protected ErrorPageCustomizer(ServerProperties properties) { this.properties = properties; } @Override public void registerErrorPages(ErrorPageRegistry errorPageRegistry) { ErrorPage errorPage = new ErrorPage(this.properties.getServletPrefix() + this.properties.getError().getPath()); errorPageRegistry.addErrorPages(errorPage); } @Override public int getOrder() { return 0; }}
将缺点页面注册到内嵌的tomcat的servlet容器中。
PreserveErrorControllerTargetClassPostProcessor实现BeanFactoryPostProcessor接口,可以修正BEAN的配置信息ErrorAutoConfiguration内的两个配置
//2个config配置@Configurationstatic class DefaultErrorViewResolverConfiguration { private final ApplicationContext applicationContext; private final ResourceProperties resourceProperties; DefaultErrorViewResolverConfiguration(ApplicationContext applicationContext, ResourceProperties resourceProperties) { this.applicationContext = applicationContext; this.resourceProperties = resourceProperties; } @Bean @ConditionalOnBean(DispatcherServlet.class) @ConditionalOnMissingBean public DefaultErrorViewResolver conventionErrorViewResolver() { return new DefaultErrorViewResolver(this.applicationContext, this.resourceProperties); }}@Configuration@ConditionalOnProperty(prefix = \"大众server.error.whitelabel\公众, name = \"大众enabled\"大众, matchIfMissing = true)@Conditional(ErrorTemplateMissingCondition.class)protected static class WhitelabelErrorViewConfiguration { private final SpelView defaultErrorView = new SpelView( \"大众<html><body><h1>Whitelabel Error Page</h1>\"大众 + \"大众<p>This application has no explicit mapping for /error, so you are seeing this as a fallback.</p>\公众 + \"大众<div id='created'>${timestamp}</div>\"大众 + \公众<div>There was an unexpected error (type=${error}, status=${status}).</div>\公众 + \"大众<div>${message}</div></body></html>\公众); @Bean(name = \"大众error\"大众) @ConditionalOnMissingBean(name = \"大众error\"大众) public View defaultErrorView() { return this.defaultErrorView; } // If the user adds @EnableWebMvc then the bean name view resolver from // WebMvcAutoConfiguration disappears, so add it back in to avoid disappointment. @Bean @ConditionalOnMissingBean(BeanNameViewResolver.class) public BeanNameViewResolver beanNameViewResolver() { BeanNameViewResolver resolver = new BeanNameViewResolver(); resolver.setOrder(Ordered.LOWEST_PRECEDENCE - 10); return resolver; }}DefaultErrorViewResolverConfiguration:默认的error视图解析配置;WhitelabelErrorViewConfiguration:默认设置了/error的页面,和Whitelabel Error Page页面相应内容。
如果Spring MVC在处理业务的过程中抛出非常,会被 Servlet 容器捕捉到,Servlet 容器再将要求转发给注册好的非常处理映射 /error 做相应处理。
springboot配置文件默认error干系配置
springboot配置文件application.properties中关于error默认配置:
server.error.include-stacktrace=never # When to include a \"大众stacktrace\"大众 attribute.server.error.path=/error # Path of the error controller.server.error.whitelabel.enabled=true # Enable the default error page displayed in browsers in case of a server error.
springboot 自定义非常处理
通过跟踪springboot对非常处理得源码跟踪,根据业务须要,可以细分前端相应的缺点页面,也可以统一利用/error页面+缺点提示信息进行处理。
根据自己的需求自定义非常处理机制;详细可履行的操作如下:
可以通过配置error/HttpStatus(缺点状态码)页面实现自定义缺点页面【底层实现,详见:BasicErrorController源码】;可以实现BasicErrorController,自定义普通要求的非常页面相应信息和异步要求的相应信息,统一利用/error页面进行缺点相应提示;自定义实现ErrorAttributes接口,覆盖DefaultErrorAttributes实现,或是继续DefaultErrorAttributes类,重写里面的方法【TODO,不推举】。1和2的方法可单独利用,也可以结合利用。
自定义非常页面
可以根据不同的缺点状态码,在前端细分不同的相应界面给用户进行提示;资源路径必须是:静态资源路径下/error/HttpStats(比如:/error/404等)
自定义非常页面<!DOCTYPE html><html lang=\公众en\公众><head> <meta charset=\"大众UTF-8\"大众></meta> <title>404交情提示</title></head><body><h1>访问的资源未找到(404)</h1></body></html>
404.html
500.html等,这里只演示404。
统一非常处理
普通要求,前端利用error页面+自定义缺点相应信息;
其他要求(异步),统一自定义缺点相应信息,规范处理异步相应的缺点判断和处理。
利用springMVC表明ControllerAdvice
/ @项目名称:wyait-manage @类名称:GlobalExceptionHandler @类描述:统一非常处理,包括【普通调用和ajax调用】 </br>ControllerAdvice来做controller内部的全局非常处理,但对付未进入controller前的非常,该处理方法是无法进行捕获处理的,SpringBoot供应了ErrorController的处理类来处理所有的非常(TODO)。 </br>1.当普通调用时,跳转到自定义的缺点页面;2.当ajax调用时,可返回约定的json数据工具,方便页面统一处理。 @创建人:wyait @创建韶光:2018年5月22日 上午11:44:55 @version: /@ControllerAdvicepublic class GlobalExceptionHandler { private static final Logger logger = LoggerFactory .getLogger(GlobalExceptionHandler.class); public static final String DEFAULT_ERROR_VIEW = \公众error\公众; / @描述:针对普通要乞降ajax异步要求的非常进行处理 @创建人:wyait @创建韶光:2018年5月22日 下午4:48:58 @param req @param e @return @throws Exception / @ExceptionHandler(value = Exception.class) @ResponseBody public ModelAndView errorHandler(HttpServletRequest request, HttpServletResponse response, Exception e) { logger.debug(getClass().getName() + \公众.errorHandler】统一非常处理:request=\"大众+request); ModelAndView mv=new ModelAndView(); logger.info(getClass().getName() + \公众.errorHandler】统一非常处理:\"大众+e.getMessage()); //1 获取缺点状态码 HttpStatus httpStatus=getStatus(request); logger.info(getClass().getName() + \"大众.errorHandler】统一非常处理!缺点状态码httpStatus:\"大众+httpStatus); //2 返回缺点提示 ExceptionEnum ee=getMessage(httpStatus); //3 将缺点信息放入mv中 mv.addObject(\公众type\"大众, ee.getType()); mv.addObject(\公众code\公众, ee.getCode()); mv.addObject(\公众msg\公众, ee.getMsg()); if(!ShiroFilterUtils.isAjax(request)){ //不是异步要求 mv.setViewName(DEFAULT_ERROR_VIEW); logger.debug(getClass().getName() + \"大众.errorHandler】统一非常处理:普通要求。\公众); } logger.debug(getClass().getName() + \"大众.errorHandler】统一非常处理相应结果:MV=\"大众+mv); return mv; } ...}
运行测试:先走GlobalExceptionHandler(利用表明@ControllerAdvice)类里面的方法,而后又实行了BasicErrorController方法;被springboot自带的BasicErrorController覆盖。
实现springboot的AbstractErrorController
自定义实现AbstractErrorController,添加相应的缺点提示信息。
@RequestMapping(produces = \"大众text/html\公众) public ModelAndView errorHtml(HttpServletRequest request, HttpServletResponse response) { ModelAndView mv = new ModelAndView(ERROR_PATH); / model工具包含了非常信息 / Map<String, Object> model = getErrorAttributes(request, isIncludeStackTrace(request, MediaType.TEXT_HTML)); // 1 获取缺点状态码(也可以根据非常工具返回对应的缺点信息) HttpStatus httpStatus = getStatus(request); // 2 返回缺点提示 ExceptionEnum ee = getMessage(httpStatus); Result<String> result = new Result<String>( String.valueOf(ee.getType()), ee.getCode(), ee.getMsg()); // 3 将缺点信息放入mv中 mv.addObject(\"大众result\"大众, result); logger.info(\公众统一非常处理【\"大众 + getClass().getName() + \公众.errorHtml】统一非常处理!缺点信息mv:\"大众 + mv); return mv; } @RequestMapping @ResponseBody //设置相应状态码为:200,结合前端约定的规范处理。也可不设置状态码,前端ajax调用利用error函数进行掌握处理 @ResponseStatus(value=HttpStatus.OK) public Result<String> error(HttpServletRequest request, Exception e) { / model工具包含了非常信息 / Map<String, Object> model = getErrorAttributes(request, isIncludeStackTrace(request, MediaType.TEXT_HTML)); // 1 获取缺点状态码(也可以根据非常工具返回对应的缺点信息) HttpStatus httpStatus = getStatus(request); // 2 返回缺点提示 ExceptionEnum ee = getMessage(httpStatus); Result<String> result = new Result<String>( String.valueOf(ee.getType()), ee.getCode(), ee.getMsg()); // 3 将缺点信息返回// ResponseEntity logger.info(\"大众统一非常处理【\"大众 + getClass().getName() + \"大众.error】统一非常处理!缺点信息result:\"大众 + result); return result; }
针对异步要求,统一指定相应状态码:200;也可以不指定,前端在处理异步要求的时候,可以通过ajax的error函数进行掌握。
这里是继续的AbstractErrorController类,自定义实现统一非常处理,也可以直接实现ErrorController接口。
前端ajax异步统一处理:
通过约定,前端ajax异步要求,进行统一的缺点处理。
/ 针对不同的缺点可结合业务自定义处理办法 @param result @returns {Boolean} /function isError(result){ var flag=true; if(result && result.status){ flag=false; if(result.status == '-1' || result.status=='-101' || result.status=='400' || result.status=='404' || result.status=='500'){ layer.alert(result.data); }else if(result.status=='403'){ layer.alert(result.data,function(){ //跳转到未授权界面 window.location.href=\公众/403\"大众; }); } } return flag;//返回true}
利用办法:
... success:function(data){ //非常过滤处理 if(isError(data)){ alert(data); } }, ...
error.html页面:
<!DOCTYPE html><html lang=\公众en\公众 xmlns:th=\"大众http://www.thymeleaf.org\"大众><head th:include=\"大众layout :: htmlhead\"大众 th:with=\"大众title='wyait后台管理'\公众> <meta charset=\公众UTF-8\"大众></meta> <title th:text=\"大众${result.status}\"大众></title></head><body><h1>出错了</h1><p><span th:text=\"大众${result.message}\"大众></span>(<span th:text=\公众${result.data}\"大众></span>)</p></body></html>
测试效果
普通要求:
异步要求:
线上get要求乱码
问题描述
前台通过html页面,发送要求到后台查询数据,在日志中打印的sql语句显示传入的参数乱码:
SELECT ... [2018-05-11 09:15:00.582][http-bio-8280-exec-2][DEBUG][org.apache.ibatis.logging.jdbc.BaseJdbcLogger][159]:==> Parameters: 1(Integer), çè´º(String)[2018-05-11 09:15:00.585][http-bio-8280-exec-2][DEBUG][org.apache.ibatis.logging.jdbc.BaseJdbcLogger][159]:<== Total: 1...
本地windows开拓环境测试没有乱码问题;
要求信息
前端页面发送get要求,浏览器默认对get要求路径进行URL编码处理。
后台Controller打印的日志
分页查询用户列表!
搜索条件:userSearch:UserSearchDTO{page=1, limit=10, uname='çç', umobile='', insertTimeStart='', insertTimeEnd=''},page:1,每页记录数量limit:10,要求编码:UTF-8
Controller层在吸收到这个uname参数时,已经是乱码,ISO-8859-1解码后的结果。
要求参数编码流程
前端页面发送get要求,浏览器默认在中文的UTF-8后加上上%得到URL编码,比如:%e8%b4%b9%e7...;get要求到tomcat运用做事器后,会以默认的ISO-8859-1进行解码;在controller中,吸收到的是经由URL编码和iso-8859-1解码后的参数值。详细编码细节:TODO
办理方案
项目编码配置【可以不配置】
开拓前,默认必须统一编码环境;正常都是设置为utf-8。
spring boot 与spring mvc不同,在web运用中,spring boot默认的编码格式为UTF-8,而spring mvc的默认编码格式为iso-8859-1。
spring boot项目中如果没有分外需求,该编码不须要修正。如果要逼迫其他编码格式,spring boot供应了设置办法:
通过application.properties配置文件设置:# 默认utf-8配置spring.http.encoding.force=truespring.http.encoding.charset=UTF-8spring.http.encoding.enabled=trueserver.tomcat.uri-encoding=UTF-8
此时拦截器中返回的中文已经不乱码了,但是controller中返回的数据可能会依旧乱码。
参考spring MVC的办法,自定义实现WebMvcConfigurerAdapter类,处理相应数据乱码问题:@Beanpublic HttpMessageConverter<String> responseBodyConverter() { StringHttpMessageConverter converter = new StringHttpMessageConverter(Charset.forName(\"大众UTF-8\"大众)); return converter;}@Overridepublic void configureMessageConverters(List<HttpMessageConverter<?>> converters) { super.configureMessageConverters(converters); converters.add(responseBodyConverter());}
也可以在controller方法@RequestMapping上添加:
produces=\"大众text/plain;charset=UTF-8\"大众
这种方法的弊端是限定了数据类型。
乱码办理方案
表单采取get办法提交,中文乱码办理方案:
改为post要求;手动编解码:param = new String(param.getBytes(\"大众iso8859-1\公众), \"大众utf-8\"大众);修正tomcat配置server.xml文件:找到如下代码:
<Connector port=\公众8080\公众 protocol=\公众HTTP/1.1\"大众 connectionTimeout=\"大众20000\"大众 redirectPort=\"大众8443\"大众 />
在这里添加一个属性:URIEncoding,将该属性值设置为UTF-8,即可让Tomcat(默认ISO-8859-1编码)以UTF-8的编码处理get要求。
修正完成后:
<Connector port=\公众8080\"大众 protocol=\"大众HTTP/1.1\公众 connectionTimeout=\公众20000\公众 redirectPort=\"大众8443\"大众 URIEncoding=\公众UTF-8\"大众 />发送get要求前,浏览器中两次URL编码: 两次编码两次解码的过程为: ==UTF-8编码->UTF-8(iso-8859-1)编码->iso-8859-1解码->UTF-8解码,编码和解码的过程是对称的,以是不会涌现乱码。==
//js代码 param = encodeURI(param); // alert(\"大众第一次URL编码:\"大众 + param); param = encodeURI(param); // alert(\"大众第二次URL编码:\公众 + param);
后台代码:
//两次解码URLDecoder.decode(URLDecoder.decode(param,\"大众utf-8\"大众),\公众utf-8\公众);总结
以上四种办理方案,可结合详细情形进行利用。
no session非常
非常日志1:
[2018-05-21 18:00:51.574][http-nio-8280-exec-6][DEBUG][org.apache.shiro.web.servlet.SimpleCookie][389]:Found 'SHRIOSESSIONID' cookie value [fc6b7b64-6c59-4f82-853b-e2ca20135b99][2018-05-21 18:00:51.575][http-nio-8280-exec-6][DEBUG][org.apache.shiro.mgt.DefaultSecurityManager][447]:Resolved SubjectContext context session is invalid. Ignoring and creating an anonymous (session-less) Subject instance.org.apache.shiro.session.UnknownSessionException: There is no session with id [fc6b7b64-6c59-4f82-853b-e2ca20135b99] at org.apache.shiro.session.mgt.eis.AbstractSessionDAO.readSession(AbstractSessionDAO.java:170) ~[shiro-all-1.3.1.jar:1.3.1]
非常日志2【偶尔涌现】:
Caused by: javax.crypto.BadPaddingException: Given final block not properly padded at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:811) ~[sunjce_provider.jar:1.7.0_85]
UnknownSessionException
UnknownSessionException: There is no session with id [...]
缘故原由
结合项目配置,剖析问题缘故原由:
1,用户退出后,浏览器中的SHIROSESSIONID依然存在;
2,再次发送要求时,携带SHIROSESSIONID,会在shiro的DefaultWebSecurityManager.getSessionKey(context)中,逐层跟踪对应在sessionManager中session值,没有的话,终极在AbstractSessionDAO.readSession(sessionID)中抛出非常。
办理方案
在程序中退出的地方,打消cookie://删除cookieCookie co = new Cookie(\"大众username\"大众, \"大众\"大众);co.setMaxAge(0);// 设置立即过期co.setPath(\"大众/\公众);// 根目录,全体网站有效servletResponse.addCookie(co);设置SimpleCookie的过期韶光,和session、ehcache缓存韶光保持同等;
@Beanpublic SimpleCookie sessionIdCookie() { //DefaultSecurityManager SimpleCookie simpleCookie = new SimpleCookie(); //如果在Cookie中设置了\"大众HttpOnly\"大众属性,那么通过程序(JS脚本、Applet等)将无法读取到Cookie信息,这样能防止XSS×××。 simpleCookie.setHttpOnly(true); simpleCookie.setName(\"大众SHRIOSESSIONID\"大众); simpleCookie.setMaxAge(864000003); return simpleCookie;}手动实现shiro的logout方法,打消浏览器cookie;重写AbstractSessionDAO.readSession方法,如果session为null,清空浏览器cookie;不做处理;实际项目运行中,不影响功能实行。
源码
源码已集成到项目中:
github源码: https://github.com/wyait/manage.git
码云:https://gitee.com/wyait/manage.git
github对应项目源码目录:wyait-manage-1.2.0
码云对应项目源码目录:wyait-manage-1.2.0
转载:https://blog.51cto.com/wyait/2125708
作者:wyait