干系:

spring boot + mybatis + layui + shiro后台权限管理系统

springboot + shiro之登录人数限定、登录判断重定向、session韶光设置

jsp里alert显示乱码springboot  shiro 权限注解同一异常处置要求乱码解决 jQuery

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