日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

log中文乱码 springboot_springboot + shiro 权限注解、统一异常处理、请求乱码解决

發(fā)布時(shí)間:2024/7/23 编程问答 19 豆豆
生活随笔 收集整理的這篇文章主要介紹了 log中文乱码 springboot_springboot + shiro 权限注解、统一异常处理、请求乱码解决 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

springboot + shiro 權(quán)限注解、統(tǒng)一異常處理、請(qǐng)求亂碼解決

前篇

后臺(tái)權(quán)限管理系統(tǒng)

20200808新版本更新

版本升級(jí)及內(nèi)容優(yōu)化版本,改動(dòng)內(nèi)容:

版本更新,springboot從1.5+升級(jí)到2.1+;

權(quán)限緩存使用redis;

驗(yàn)證碼使用redis;

權(quán)限驗(yàn)證完善。

相關(guān):

基于前篇,新增功能:

新增shiro權(quán)限注解;

請(qǐng)求亂碼問(wèn)題解決;

統(tǒng)一異常處理。

源碼已集成到項(xiàng)目中:

shiro注解的使用

shiro權(quán)限注解

Shiro 提供了相應(yīng)的注解用于權(quán)限控制,如果使用這些注解就需要使用AOP 的功能來(lái)進(jìn)行判斷,如Spring AOP;Shiro 提供了Spring AOP 集成用于權(quán)限注解的解析和驗(yàn)證。

@RequiresAuthentication

表示當(dāng)前Subject已經(jīng)通過(guò)login 進(jìn)行了身份驗(yàn)證;即Subject.isAuthenticated()返回true。

@RequiresUser

表示當(dāng)前Subject已經(jīng)身份驗(yàn)證或者通過(guò)記住我登錄的。

@RequiresGuest

表示當(dāng)前Subject沒(méi)有身份驗(yàn)證或通過(guò)記住我登錄過(guò),即是游客身份。

@RequiresRoles(value={“admin”, “user”}, logical= Logical.AND)

@RequiresRoles(value={“admin”})

@RequiresRoles({“admin“})

表示當(dāng)前Subject需要角色admin 和user。

@RequiresPermissions (value={“user:a”, “user:b”}, logical= Logical.OR)

表示當(dāng)前Subject需要權(quán)限user:a或user:b。

Shiro的認(rèn)證注解處理是有內(nèi)定的處理順序的,如果有多個(gè)注解的話,前面的通過(guò)了會(huì)繼續(xù)檢查后面的,若不通過(guò)則直接返回,處理順序依次為(與實(shí)際聲明順序無(wú)關(guān)):

RequiresRoles

RequiresPermissions

RequiresAuthentication

RequiresUser

RequiresGuest

以上注解既可以用在controller中,也可以用在service中使用;

建議將shiro注解放在controller中,因?yàn)槿绻鹲ervice層使用了spring的事物注解,那么shiro注解將無(wú)效。

shiro權(quán)限注解springAOP配置

shiro權(quán)限注解要生效,必須配置springAOP通過(guò)設(shè)置shiro的SecurityManager進(jìn)行權(quán)限驗(yàn)證。

/**

*

* @描述:開(kāi)啟Shiro的注解(如@RequiresRoles,@RequiresPermissions),需借助SpringAOP掃描使用Shiro注解的類(lèi),并在必要時(shí)進(jìn)行安全邏輯驗(yàn)證

* 配置以下兩個(gè)bean(DefaultAdvisorAutoProxyCreator和AuthorizationAttributeSourceAdvisor)即可實(shí)現(xiàn)此功能

* Enable Shiro Annotations for Spring-configured beans. Only run after the lifecycleBeanProcessor(保證實(shí)現(xiàn)了Shiro內(nèi)部lifecycle函數(shù)的bean執(zhí)行) has run

* 不使用注解的話,可以注釋掉這兩個(gè)配置

* @創(chuàng)建人:wyait

* @創(chuàng)建時(shí)間: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異常處理原理

場(chǎng)景:當(dāng)用戶(hù)正常訪問(wèn)網(wǎng)站時(shí),因?yàn)槟撤N原因后端出現(xiàn)exception的時(shí)候,直接暴露異常信息或頁(yè)面顯示給用戶(hù);

這種操作體驗(yàn)不是我們想要的。所以要對(duì)異常進(jìn)行統(tǒng)一管理,能提高用戶(hù)體驗(yàn)的同時(shí),后臺(tái)能詳細(xì)定位到異常的問(wèn)題點(diǎn)。

springboot異常概況

Spring Boot提供了默認(rèn)的統(tǒng)一錯(cuò)誤頁(yè)面,這是Spring MVC沒(méi)有提供的。在理解了Spring Boot提供的錯(cuò)誤處理相關(guān)內(nèi)容之后,我們可以方便的定義自己的錯(cuò)誤返回的格式和內(nèi)容。

編寫(xiě)by zero異常

在home頁(yè)面,手動(dòng)創(chuàng)建兩個(gè)異常:普通異常和異步異常!

前端頁(yè)面:

普通請(qǐng)求異常:

點(diǎn)擊

ajax異步請(qǐng)求異常:

點(diǎn)擊

...

//js代碼

function ajaxError(){

$.get("/error/ajaxError",function(data){

layer.alert(data);

});

}

后端代碼:

/**

*

* @描述:普通請(qǐng)求異常

* @創(chuàng)建人:wyait

* @創(chuàng)建時(shí)間:2018年5月24日 下午5:30:50

*/

@RequestMapping("getError")

public void toError(){

System.out.println(1/0);

}

/**

*

* @描述:異步異常

* @創(chuàng)建人:wyait

* @創(chuàng)建時(shí)間:2018年5月24日 下午5:30:39

*/

@RequestMapping("ajaxError")

@ResponseBody

public String ajaxError(){

System.out.println(1/0);

return "異步請(qǐng)求成功!";

}

異常效果

普通異常:

console錯(cuò)誤信息:

[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'

...

通過(guò)日志可知,springboot返回的錯(cuò)誤頁(yè)面,是通過(guò):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> 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> 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

...

通過(guò)日志可知,springboot返回的錯(cuò)誤信息,是通過(guò):org.springframework.boot.autoconfigure.web.BasicErrorController.error處理返回ResponseEntity。

異常都是通過(guò)org.springframework.boot.autoconfigure.web.BasicErrorController控制處理的。

springboot異常處理解析

查看org.springframework.boot.autoconfigure.web包下面的類(lèi),跟蹤springboot對(duì)error異常處理機(jī)制。自動(dòng)配置通過(guò)一個(gè)MVC error控制器處理錯(cuò)誤

通過(guò)spring-boot-autoconfigure引入

查看springboot 處理error的類(lèi)

springboot的自動(dòng)配置,在web中處理error相關(guān)的自動(dòng)配置類(lèi):ErrorMvcAutoConfiguration。查看與處理error相關(guān)的類(lèi):

ErrorMvcAutoConfiguration.class

ErrorAttibutes.class

ErrorController.class

ErrorProperties.class

ErrorViewResolver.class

...

ErrorAutoConfiguration類(lèi)源碼//TODO

ErrorAutoConfiguration注冊(cè)的bean

//4個(gè)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();

}

DefaultErrorAttributes類(lèi)

@Order(Ordered.HIGHEST_PRECEDENCE)

public class DefaultErrorAttributes

implements ErrorAttributes, HandlerExceptionResolver, Ordered {

...

}

ErrorAttributes:

public interface ErrorAttributes {

Map 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類(lèi):

實(shí)現(xiàn)了ErrorAttributes接口,當(dāng)處理/error錯(cuò)誤頁(yè)面時(shí),可以在該bean中讀取錯(cuò)誤信息響應(yīng)返回;

實(shí)現(xiàn)了HandlerExceptionResolver接口。

debug跟蹤源碼:即DispatcherServlet在doDispatch過(guò)程中有異常拋出時(shí):

一. 先由HandlerExceptionResolver.resolveException解析異常并保存在request中;

二. 再DefaultErrorAttributes.getErrorAttributes處理;DefaultErrorAttributes在處理過(guò)程中,從request中獲取錯(cuò)誤信息,將錯(cuò)誤信息保存到RequestAttributes中;

三. 最后在獲取錯(cuò)誤信息getError(RequestAttributes)時(shí),從RequestAttributes中取到錯(cuò)誤信息。

BasicErrorController類(lèi)

@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 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> error(HttpServletRequest request) {

Map body = getErrorAttributes(request,

isIncludeStackTrace(request, MediaType.ALL));

HttpStatus status = getStatus(request);

return new ResponseEntity>(body, status);

}

...

}

resolveErrorView方法(查找=error/“錯(cuò)誤狀態(tài)碼”;的資源):

如果不是異常請(qǐng)求,會(huì)執(zhí)行resolveErrorView方法;該方法會(huì)先在默認(rèn)或配置的靜態(tài)資源路徑下查找error/HttpStatus(錯(cuò)誤狀態(tài)碼)的資源文件,如果沒(méi)有;使用默認(rèn)的error頁(yè)面。

public class DefaultErrorViewResolver implements ErrorViewResolver, Ordered {

...

@Override

public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status,

Map model) {

//status:異常錯(cuò)誤狀態(tài)碼

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 model) {

//視圖名稱(chēng),默認(rèn)是error/+“status”錯(cuò)誤狀態(tài)碼;比如: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等頁(yè)面

private ModelAndView resolveResource(String viewName, Map 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根據(jù)Accept頭的內(nèi)容,輸出不同格式的錯(cuò)誤響應(yīng)。比如針對(duì)瀏覽器的請(qǐng)求生成html頁(yè)面,針對(duì)其它請(qǐng)求生成json格式的返回。

可以通過(guò)配置error/HttpStatus頁(yè)面實(shí)現(xiàn)自定義錯(cuò)誤頁(yè)面。

ErrorPageCustomizer類(lèi)

/**

* {@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;

}

}

將錯(cuò)誤頁(yè)面注冊(cè)到內(nèi)嵌的tomcat的servlet容器中。

PreserveErrorControllerTargetClassPostProcessor實(shí)現(xiàn)BeanFactoryPostProcessor接口,可以修改BEAN的配置信息

ErrorAutoConfiguration內(nèi)的兩個(gè)配置

//2個(gè)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(

"

Whitelabel Error Page

"

+ "

This application has no explicit mapping for /error, so you are seeing this as a fallback.

"

+ "

${timestamp}"

+ "

There was an unexpected error (type=${error}, status=${status})."

+ "

${message}");

@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:默認(rèn)的error視圖解析配置;

WhitelabelErrorViewConfiguration:默認(rèn)設(shè)置了/error的頁(yè)面,和Whitelabel Error Page頁(yè)面響應(yīng)內(nèi)容。

如果Spring MVC在處理業(yè)務(wù)的過(guò)程中拋出異常,會(huì)被 Servlet 容器捕捉到,Servlet 容器再將請(qǐng)求轉(zhuǎn)發(fā)給注冊(cè)好的異常處理映射 /error 做響應(yīng)處理。

springboot配置文件默認(rèn)error相關(guān)配置

springboot配置文件application.properties中關(guān)于error默認(rèn)配置:

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 自定義異常處理

通過(guò)跟蹤springboot對(duì)異常處理得源碼跟蹤,根據(jù)業(yè)務(wù)需要,可以細(xì)分前端響應(yīng)的錯(cuò)誤頁(yè)面,也可以統(tǒng)一使用/error頁(yè)面+錯(cuò)誤提示信息進(jìn)行處理。

根據(jù)自己的需求自定義異常處理機(jī)制;具體可實(shí)施的操作如下:

可以通過(guò)配置error/HttpStatus(錯(cuò)誤狀態(tài)碼)頁(yè)面實(shí)現(xiàn)自定義錯(cuò)誤頁(yè)面【底層實(shí)現(xiàn),詳見(jiàn):BasicErrorController源碼】;

可以實(shí)現(xiàn)BasicErrorController,自定義普通請(qǐng)求的異常頁(yè)面響應(yīng)信息和異步請(qǐng)求的響應(yīng)信息,統(tǒng)一使用/error頁(yè)面進(jìn)行錯(cuò)誤響應(yīng)提示;

自定義實(shí)現(xiàn)ErrorAttributes接口,覆蓋DefaultErrorAttributes實(shí)現(xiàn),或是繼承DefaultErrorAttributes類(lèi),重寫(xiě)里面的方法【TODO,不推薦】。

1和2的方法可單獨(dú)使用,也可以結(jié)合使用。

自定義異常頁(yè)面

可以根據(jù)不同的錯(cuò)誤狀態(tài)碼,在前端細(xì)分不同的響應(yīng)界面給用戶(hù)進(jìn)行提示;資源路徑必須是:靜態(tài)資源路徑下/error/HttpStats(比如:/error/404等)

自定義異常頁(yè)面

404友情提示

訪問(wèn)的資源未找到(404)

404.html

500.html等,這里只演示404。

統(tǒng)一異常處理

普通請(qǐng)求,前端使用error頁(yè)面+自定義錯(cuò)誤響應(yīng)信息;

其他請(qǐng)求(異步),統(tǒng)一自定義錯(cuò)誤響應(yīng)信息,規(guī)范處理異步響應(yīng)的錯(cuò)誤判斷和處理。

使用springMVC注解ControllerAdvice

/**

*

* @項(xiàng)目名稱(chēng):wyait-manage

* @類(lèi)名稱(chēng):GlobalExceptionHandler

* @類(lèi)描述:統(tǒng)一異常處理,包括【普通調(diào)用和ajax調(diào)用】

* ControllerAdvice來(lái)做controller內(nèi)部的全局異常處理,但對(duì)于未進(jìn)入controller前的異常,該處理方法是無(wú)法進(jìn)行捕獲處理的,SpringBoot提供了ErrorController的處理類(lèi)來(lái)處理所有的異常(TODO)。

* 1.當(dāng)普通調(diào)用時(shí),跳轉(zhuǎn)到自定義的錯(cuò)誤頁(yè)面;2.當(dāng)ajax調(diào)用時(shí),可返回約定的json數(shù)據(jù)對(duì)象,方便頁(yè)面統(tǒng)一處理。

* @創(chuàng)建人:wyait

* @創(chuàng)建時(shí)間:2018年5月22日 上午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";

/**

*

* @描述:針對(duì)普通請(qǐng)求和ajax異步請(qǐng)求的異常進(jìn)行處理

* @創(chuàng)建人:wyait

* @創(chuàng)建時(shí)間: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】統(tǒng)一異常處理:request="+request);

ModelAndView mv=new ModelAndView();

logger.info(getClass().getName() + ".errorHandler】統(tǒng)一異常處理:"+e.getMessage());

//1 獲取錯(cuò)誤狀態(tài)碼

HttpStatus httpStatus=getStatus(request);

logger.info(getClass().getName() + ".errorHandler】統(tǒng)一異常處理!錯(cuò)誤狀態(tài)碼httpStatus:"+httpStatus);

//2 返回錯(cuò)誤提示

ExceptionEnum ee=getMessage(httpStatus);

//3 將錯(cuò)誤信息放入mv中

mv.addObject("type", ee.getType());

mv.addObject("code", ee.getCode());

mv.addObject("msg", ee.getMsg());

if(!ShiroFilterUtils.isAjax(request)){

//不是異步請(qǐng)求

mv.setViewName(DEFAULT_ERROR_VIEW);

logger.debug(getClass().getName() + ".errorHandler】統(tǒng)一異常處理:普通請(qǐng)求。");

}

logger.debug(getClass().getName() + ".errorHandler】統(tǒng)一異常處理響應(yīng)結(jié)果:MV="+mv);

return mv;

}

...

}

運(yùn)行測(cè)試:先走GlobalExceptionHandler(使用注解@ControllerAdvice)類(lèi)里面的方法,而后又執(zhí)行了BasicErrorController方法;被springboot自帶的BasicErrorController覆蓋。

實(shí)現(xiàn)springboot的AbstractErrorController

自定義實(shí)現(xiàn)AbstractErrorController,添加響應(yīng)的錯(cuò)誤提示信息。

@RequestMapping(produces = "text/html")

public ModelAndView errorHtml(HttpServletRequest request,

HttpServletResponse response) {

ModelAndView mv = new ModelAndView(ERROR_PATH);

/** model對(duì)象包含了異常信息 */

Map model = getErrorAttributes(request,

isIncludeStackTrace(request, MediaType.TEXT_HTML));

// 1 獲取錯(cuò)誤狀態(tài)碼(也可以根據(jù)異常對(duì)象返回對(duì)應(yīng)的錯(cuò)誤信息)

HttpStatus httpStatus = getStatus(request);

// 2 返回錯(cuò)誤提示

ExceptionEnum ee = getMessage(httpStatus);

Result result = new Result(

String.valueOf(ee.getType()), ee.getCode(), ee.getMsg());

// 3 將錯(cuò)誤信息放入mv中

mv.addObject("result", result);

logger.info("統(tǒng)一異常處理【" + getClass().getName()

+ ".errorHtml】統(tǒng)一異常處理!錯(cuò)誤信息mv:" + mv);

return mv;

}

@RequestMapping

@ResponseBody

//設(shè)置響應(yīng)狀態(tài)碼為:200,結(jié)合前端約定的規(guī)范處理。也可不設(shè)置狀態(tài)碼,前端ajax調(diào)用使用error函數(shù)進(jìn)行控制處理

@ResponseStatus(value=HttpStatus.OK)

public Result error(HttpServletRequest request, Exception e) {

/** model對(duì)象包含了異常信息 */

Map model = getErrorAttributes(request,

isIncludeStackTrace(request, MediaType.TEXT_HTML));

// 1 獲取錯(cuò)誤狀態(tài)碼(也可以根據(jù)異常對(duì)象返回對(duì)應(yīng)的錯(cuò)誤信息)

HttpStatus httpStatus = getStatus(request);

// 2 返回錯(cuò)誤提示

ExceptionEnum ee = getMessage(httpStatus);

Result result = new Result(

String.valueOf(ee.getType()), ee.getCode(), ee.getMsg());

// 3 將錯(cuò)誤信息返回

// ResponseEntity

logger.info("統(tǒng)一異常處理【" + getClass().getName()

+ ".error】統(tǒng)一異常處理!錯(cuò)誤信息result:" + result);

return result;

}

針對(duì)異步請(qǐng)求,統(tǒng)一指定響應(yīng)狀態(tài)碼:200;也可以不指定,前端在處理異步請(qǐng)求的時(shí)候,可以通過(guò)ajax的error函數(shù)進(jìn)行控制。

這里是繼承的AbstractErrorController類(lèi),自定義實(shí)現(xiàn)統(tǒng)一異常處理,也可以直接實(shí)現(xiàn)ErrorController接口。

前端ajax異步統(tǒng)一處理:

通過(guò)約定,前端ajax異步請(qǐng)求,進(jìn)行統(tǒng)一的錯(cuò)誤處理。

/**

* 針對(duì)不同的錯(cuò)誤可結(jié)合業(yè)務(wù)自定義處理方式

* @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(){

//跳轉(zhuǎn)到未授權(quán)界面

window.location.href="/403";

});

}

}

return flag;//返回true

}

使用方式:

...

success:function(data){

//異常過(guò)濾處理

if(isError(data)){

alert(data);

}

},

...

error.html頁(yè)面:

出錯(cuò)了

()

測(cè)試效果

普通請(qǐng)求:

異步請(qǐng)求:

線上get請(qǐng)求亂碼

問(wèn)題描述

前臺(tái)通過(guò)html頁(yè)面,發(fā)送請(qǐng)求到后臺(tái)查詢(xún)數(shù)據(jù),在日志中打印的sql語(yǔ)句顯示傳入的參數(shù)亂碼:

SELECT ...

[2018-05-11 09:15:00.582][http-bio-8280-exec-2][DEBUG][org.apache.ibatis.logging.jdbc.BaseJdbcLogger][159]:==> Parameters: 1(Integer), ?è′o(String)

[2018-05-11 09:15:00.585][http-bio-8280-exec-2][DEBUG][org.apache.ibatis.logging.jdbc.BaseJdbcLogger][159]:<== Total: 1

...

本地windows開(kāi)發(fā)環(huán)境測(cè)試沒(méi)有亂碼問(wèn)題;

請(qǐng)求信息

前端頁(yè)面發(fā)送get請(qǐng)求,瀏覽器默認(rèn)對(duì)get請(qǐng)求路徑進(jìn)行URL編碼處理。

后臺(tái)Controller打印的日志

分頁(yè)查詢(xún)用戶(hù)列表!搜索條件:userSearch:UserSearchDTO{page=1, limit=10, uname='??', umobile='', insertTimeStart='', insertTimeEnd=''},page:1,每頁(yè)記錄數(shù)量limit:10,請(qǐng)求編碼:UTF-8

Controller層在接收到這個(gè)uname參數(shù)時(shí),已經(jīng)是亂碼,ISO-8859-1解碼后的結(jié)果。

請(qǐng)求參數(shù)編碼流程

前端頁(yè)面發(fā)送get請(qǐng)求,瀏覽器默認(rèn)在中文的UTF-8后加上上%得到URL編碼,比如:%e8%b4%b9%e7...;

get請(qǐng)求到tomcat應(yīng)用服務(wù)器后,會(huì)以默認(rèn)的ISO-8859-1進(jìn)行解碼;

在controller中,接收到的是經(jīng)過(guò)URL編碼和iso-8859-1解碼后的參數(shù)值。

具體編碼細(xì)節(jié):TODO

解決方案

項(xiàng)目編碼配置【可以不配置】

開(kāi)發(fā)前,默認(rèn)必須統(tǒng)一編碼環(huán)境;正常都是設(shè)置為utf-8。

spring boot 與spring mvc不同,在web應(yīng)用中,spring boot默認(rèn)的編碼格式為UTF-8,而spring mvc的默認(rèn)編碼格式為iso-8859-1。

spring boot項(xiàng)目中如果沒(méi)有特殊需求,該編碼不需要修改。如果要強(qiáng)制其他編碼格式,spring boot提供了設(shè)置方式:

通過(guò)application.properties配置文件設(shè)置:

# 默認(rèn)utf-8配置

spring.http.encoding.force=true

spring.http.encoding.charset=UTF-8

spring.http.encoding.enabled=true

server.tomcat.uri-encoding=UTF-8

此時(shí)攔截器中返回的中文已經(jīng)不亂碼了,但是controller中返回的數(shù)據(jù)可能會(huì)依舊亂碼。

參考spring MVC的方式,自定義實(shí)現(xiàn)WebMvcConfigurerAdapter類(lèi),處理響應(yīng)數(shù)據(jù)亂碼問(wèn)題:

@Bean

public HttpMessageConverter responseBodyConverter() {

StringHttpMessageConverter converter = new StringHttpMessageConverter(Charset.forName("UTF-8"));

return converter;

}

@Override

public void configureMessageConverters(List> converters) {

super.configureMessageConverters(converters);

converters.add(responseBodyConverter());

}

也可以在controller方法@RequestMapping上添加:

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

這種方法的弊端是限定了數(shù)據(jù)類(lèi)型。

亂碼解決方案

表單采用get方式提交,中文亂碼解決方案:

改為post請(qǐng)求;

手動(dòng)編解碼:

param = new String(param.getBytes("iso8859-1"), "utf-8");

修改tomcat配置server.xml文件:

找到如下代碼:

在這里添加一個(gè)屬性:URIEncoding,將該屬性值設(shè)置為UTF-8,即可讓Tomcat(默認(rèn)ISO-8859-1編碼)以UTF-8的編碼處理get請(qǐng)求。

修改完成后:

發(fā)送get請(qǐng)求前,瀏覽器中兩次URL編碼:

兩次編碼兩次解碼的過(guò)程為:

==UTF-8編碼->UTF-8(iso-8859-1)編碼->iso-8859-1解碼->UTF-8解碼,編碼和解碼的過(guò)程是對(duì)稱(chēng)的,所以不會(huì)出現(xiàn)亂碼。==

//js代碼

param = encodeURI(param);

// alert("第一次URL編碼:" + param);

param = encodeURI(param);

// alert("第二次URL編碼:" + param);

后臺(tái)代碼:

//兩次解碼

URLDecoder.decode(URLDecoder.decode(param,"utf-8"),"utf-8");

總結(jié)

以上四種解決方案,可結(jié)合具體情況進(jìn)行使用。

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【偶爾出現(xiàn)】:

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 [...]

原因

結(jié)合項(xiàng)目配置,分析問(wèn)題原因:

1,用戶(hù)退出后,瀏覽器中的SHIROSESSIONID依然存在;

2,再次發(fā)送請(qǐng)求時(shí),攜帶SHIROSESSIONID,會(huì)在shiro的DefaultWebSecurityManager.getSessionKey(context)中,逐層跟蹤對(duì)應(yīng)在sessionManager中session值,沒(méi)有的話,最終在AbstractSessionDAO.readSession(sessionID)中拋出異常。

解決方案

在程序中退出的地方,清除cookie:

//刪除cookie

Cookie co = new Cookie("username", "");

co.setMaxAge(0);// 設(shè)置立即過(guò)期

co.setPath("/");// 根目錄,整個(gè)網(wǎng)站有效

servletResponse.addCookie(co);

設(shè)置SimpleCookie的過(guò)期時(shí)間,和session、ehcache緩存時(shí)間保持一致;

@Bean

public SimpleCookie sessionIdCookie() {

//DefaultSecurityManager

SimpleCookie simpleCookie = new SimpleCookie();

//如果在Cookie中設(shè)置了"HttpOnly"屬性,那么通過(guò)程序(JS腳本、Applet等)將無(wú)法讀取到Cookie信息,這樣能防止XSS×××。

simpleCookie.setHttpOnly(true);

simpleCookie.setName("SHRIOSESSIONID");

simpleCookie.setMaxAge(86400000*3);

return simpleCookie;

}

手動(dòng)實(shí)現(xiàn)shiro的logout方法,清除瀏覽器cookie;

重寫(xiě)AbstractSessionDAO.readSession方法,如果session為null,清空瀏覽器cookie;

不做處理;實(shí)際項(xiàng)目運(yùn)行中,不影響功能執(zhí)行。

源碼

源碼已集成到項(xiàng)目中:

前篇

20200808新版本更新

版本升級(jí)及內(nèi)容優(yōu)化版本,改動(dòng)內(nèi)容:

版本更新,springboot從1.5+升級(jí)到2.1+;

權(quán)限緩存使用redis;

驗(yàn)證碼使用redis;

權(quán)限驗(yàn)證完善。

總結(jié)

以上是生活随笔為你收集整理的log中文乱码 springboot_springboot + shiro 权限注解、统一异常处理、请求乱码解决的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。