在之前项目中,整合过无数次的SSM,但都没有去仔细分析SpringMVC结合Shiro注解实现权限控制的具体流程,刚好今天处理系统的Ajax非法请求的异常信息处理,现将其执行流程以及原理记录如下:
1、启用Shiro注解
系统中如果需要使用Shiro的注解来控制方法级的权限,则需要做如下的配置:
配置中的AuthorizationAttributeSourceAdvisor切面类中创建了一个AOP配置类,该配置类专门处理Shiro权限注解的方法拦截(AopAllianceAnnotationsAuthorizingMethodInterceptor)。核心代码如下:
public AopAllianceAnnotationsAuthorizingMethodInterceptor() {
List interceptors =
new ArrayList(5);
//use a Spring-specific Annotation resolver - Spring's AnnotationUtils is nicer than the
//raw JDK resolution process.
AnnotationResolver resolver = new SpringAnnotationResolver();
//we can re-use the same resolver instance - it does not retain state:
interceptors.add(new RoleAnnotationMethodInterceptor(resolver));
interceptors.add(new PermissionAnnotationMethodInterceptor(resolver));
interceptors.add(new AuthenticatedAnnotationMethodInterceptor(resolver));
interceptors.add(new UserAnnotationMethodInterceptor(resolver));
interceptors.add(new GuestAnnotationMethodInterceptor(resolver));
setMethodInterceptors(interceptors);
}
/**
* Creates a Shiro {@link MethodInvocation MethodInvocation} instance and then immediately calls
* {@link org.apache.shiro.authz.aop.AuthorizingMethodInterceptor#invoke super.invoke}.
*
* @param methodInvocation the AOP Alliance-specific methodInvocation
instance.
* @return the return value from invoking the method invocation.
* @throws Throwable if the underlying AOP Alliance method invocation throws a Throwable
.
*/
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
org.apache.shiro.aop.MethodInvocation mi = createMethodInvocation(methodInvocation);
return super.invoke(mi);
}
2、配置Shiro拦截器和SpringMVC的核心控制器
shiroFilter
org.springframework.web.filter.DelegatingFilterProxy
true
targetFilterLifecycle
true
shiroFilter
/*
dispatcherServlet
org.springframework.web.servlet.DispatcherServlet
contextConfigLocation
classpath:spring/applicationContext-mvc.xml
dispatcherServlet
/
请求进入系统后,先执行Shiro的拦截器链AbstractShiroFilter,Shiro拦截器链执行完过后执行dispatcherServlet的方法。
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// 获取SpringMVC的Handler(RequestMappingHandlerAdapter)
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);
return;
}
// Determine handler adapter for the current request.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (logger.isDebugEnabled()) {
logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
}
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// Actually invoke the handler.
// 执行Controller
// 触发Shiro注解拦截器AuthorizingAnnotationMethodInterceptor
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
applyDefaultViewName(request, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
// 出现认证异常UnauthorizedException,寻找异常控制器
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Error err) {
triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err);
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
3、编写异常处理Handler
/**
* @Description: 认证失败异常处理
* @return String
*/
@ExceptionHandler({ UnauthenticatedException.class, AuthenticationException.class })
public String authenticationException(HttpServletRequest request, HttpServletResponse response)
{
if (isAjaxRequest(request))
{
AppResult appResult = AppResult.error("系统认证失败,拒绝访问!");
ResponseUtil.renderJson(response, JSON.toJSONString(appResult));
return null;
}
else
return "error/401";
}
/**
* @Description: 授权失败异常处理
* @return String
*/
@ExceptionHandler({ UnauthorizedException.class, AuthorizationException.class })
public String authorizationException(HttpServletRequest request, HttpServletResponse response)
{
if (isAjaxRequest(request))
{
AppResult appResult = AppResult.error("系统授权失败,拒绝访问!");
ResponseUtil.renderJson(response, JSON.toJSONString(appResult));
return null;
}
else
return "error/401";
}
public static boolean isAjaxRequest(HttpServletRequest request)
{
String requestedWith = request.getHeader("x-requested-with");
return StringUtils.isNotEmpty(requestedWith) && requestedWith.equalsIgnoreCase("XMLHttpRequest");
}
格式有点乱,仅作为流程分析参考。