springboot + shiro 权限注解、一致反常处理、恳求乱码处理51CTO博客 - AG环亚娱乐集团

springboot + shiro 权限注解、一致反常处理、恳求乱码处理51CTO博客

2019-01-03 10:39:44 | 作者: 吉星 | 标签: 处理,反常,恳求 | 浏览: 2215

springboot + shiro 权限注解、一致反常处理、恳求乱码处理 前篇 后台权限办理体系

相关:

  • spring boot + mybatis + layui + shiro后台权限办理体系
  • springboot + shiro之登录人数约束、登录判别重定向、session时刻设置
  • springboot + shiro 动态更新用户信息

依据前篇,新增功用:

  1. 新增shiro权限注解;
  2. 恳求乱码问题处理;
  3. 一致反常处理。

源码已集成到项目中:

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的认证注解处理是有内定的处理次序的,假如有多个注解的话,前面的经过了会持续检查后边的,若不经过则直接回来,处理次序依次为(与实践声明次序无关):

RequiresRoles
RequiresPermissions
RequiresAuthentication
RequiresUser
RequiresGuest

以上注解既能够用在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年 下午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:30:50
 */
@RequestMapping("getError")
public void toError(){
    System.out.println(1/0);
}
/**
 * 
 * @描绘:异步反常
 * @创立人:wyait
 * @创立时刻:2018年 下午5:30:39
 */
@RequestMapping("ajaxError")
@ResponseBody
public 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 cause
java.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 cause
java.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.class
  • ErrorAttibutes.class
  • ErrorController.class
  • ErrorProperties.class
  • ErrorViewResolver.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);
}

@Bean
public ErrorPageCustomizer errorPageCustomizer() {
    return new ErrorPageCustomizer(this.serverProperties);
}

@Bean
public static PreserveErrorControllerTargetClassPostProcessor preserveErrorControllerTargetClassPostProcessor() {
    return new PreserveErrorControllerTargetClassPostProcessor();
}
  1. 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中取到过错信息。

  1. 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页面完成自界说过错页面。

  1. ErrorPageCustomizer类
/**
 * {@link EmbeddedServletContainerCustomizer} that configures the containers 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容器中。

  1. PreserveErrorControllerTargetClassPostProcessor完成BeanFactoryPostProcessor接口,能够修正BEAN的装备信息
ErrorAutoConfiguration内的两个装备
//2个config装备
@Configuration
static 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;
    }

}
  1. DefaultErrorViewResolverConfiguration:默许的error视图解析装备;

  2. 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页面+过错提示信息进行处理。

依据自己的需求自界说反常处理机制;详细可施行的操作如下:

  1. 能够经过装备error/HttpStatus(过错状况码)页面完成自界说过错页面【底层完成,详见:BasicErrorController源码】;

  2. 能够完成BasicErrorController,自界说一般恳求的反常页面呼应信息和异步恳求的呼应信息,一致运用/error页面进行过错呼应提示;

  3. 自界说完成ErrorAttributes接口,掩盖DefaultErrorAttributes完成,或是承继DefaultErrorAttributes类,重写里边的办法【TODO,不引荐】。

1和2的办法可独自运用,也能够结合运用。

自界说反常页面

能够依据不同的过错状况码,在前端细分不同的呼应界面给用户进行提示;资源途径有必要是:静态资源途径下/error/HttpStats(比方:/error/404等)

  1. 自界说反常页面
<!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年 上午11:44:55 
 * @version:
 */
@ControllerAdvice
public class GlobalExceptionHandler {

    private static final Logger logger = LoggerFactory
            .getLogger(GlobalExceptionHandler.class);

    public static final String DEFAULT_ERROR_VIEW = "error";

    /**
     * 
     * @描绘:针对一般恳求和ajax异步恳求的反常进行处理
     * @创立人:wyait
     * @创立时刻:2018年 下午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解码后的成果。

恳求参数编码流程
  1. 前端页面发送get恳求,浏览器默许在中文的UTF-8后加上上%得到URL编码,比方:%e8%b4%b9%e7...;
  2. get恳求到tomcat运用服务器后,会以默许的ISO-8859-1进行解码;
  3. 在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供给了设置办法:

  1. 经过application.properties装备文件设置:
# 默许utf-8装备
spring.http.encoding.force=true
spring.http.encoding.charset=UTF-8
spring.http.encoding.enabled=true
server.tomcat.uri-encoding=UTF-8

此刻拦截器中回来的中文现已不乱码了,可是controller中回来的数据可能会仍旧乱码。

  1. 参阅spring MVC的办法,自界说完成WebMvcConfigurerAdapter类,处理呼应数据乱码问题:
@Bean
public HttpMessageConverter<String> responseBodyConverter() {
    StringHttpMessageConverter converter = new StringHttpMessageConverter(Charset.forName("UTF-8"));
    return converter;
}

@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
    super.configureMessageConverters(converters);
    converters.add(responseBodyConverter());
}

也能够在controller办法@RequestMapping上增加:

produces="text/plain;charset=UTF-8"

这种办法的坏处是约束了数据类型。

乱码处理方案

表单选用get办法提交,中文乱码处理方案:

  1. 改为post恳求;
  2. 手动编解码:
param = new String(param.getBytes("iso8859-1"), "utf-8");
  1. 修正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" />
  1. 发送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("第2次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)中抛出反常。

处理方案
  1. 在程序中退出的当地,铲除cookie:
//删去cookie
Cookie co = new Cookie("username", "");
co.setMaxAge(0);// 设置当即过期
co.setPath("/");// 根目录,整个网站有用
servletResponse.addCookie(co);
  1. 设置SimpleCookie的过期时刻,和session、ehcache缓存时刻保持一致;
@Bean
public SimpleCookie sessionIdCookie() {
    //DefaultSecurityManager
    SimpleCookie simpleCookie = new SimpleCookie();
    //假如在Cookie中设置了"HttpOnly"特点,那么经进程序(JS脚本、Applet等)将无法读取到Cookie信息,这样能避免XSS×××。
    simpleCookie.setHttpOnly(true);
    simpleCookie.setName("SHRIOSESSIONID");
    simpleCookie.setMaxAge(86400000*3);
    return simpleCookie;
}
  1. 手动完成shiro的logout办法,铲除浏览器cookie;

  2. 重写AbstractSessionDAO.readSession办法,假如session为null,清空浏览器cookie;
  3. 不做处理;实践项目运转中,不影响功用履行。
源码

源码已集成到项目中:

github源码: https://github.com/wyait/manage.git
码云:https://gitee.com/wyait/manage.git
github对应项目源码目录:wyait-manage-1.2.0
码云对应项目源码目录:wyait-manage-1.2.0

前篇
  • spring boot + mybatis + layui + shiro后台权限办理体系
  • springboot + shiro之登录人数约束、登录判别重定向、session时刻设置
  • springboot + shiro 动态更新用户信息
版权声明
本文来源于网络,版权归原作者所有,其内容与观点不代表AG环亚娱乐集团立场。转载文章仅为传播更有价值的信息,如采编人员采编有误或者版权原因,请与我们联系,我们核实后立即修改或删除。

猜您喜欢的文章

阅读排行

  • 1

    简历打分排序ITeye

    排序,简历,体系
  • 2

    如何用Redlock完成分布式锁ITeye

    分布式,完成,获取
  • 3
  • 4

    java 批量推送 iosITeye

    推送,测验,内容
  • 5
  • 6
  • 7
  • 8

    递归算法和文件行列算法ITeye

    文件,行列,文件夹
  • 9

    链表结构ITeye

    结点,保存,删去
  • 10