本文是《轻量级 Java Web 框架架构设计》的系列博文。
今天想和大家简单的分享一下,在 Smart 中是如何做到访问安全控制的。也就是说,当没有登录或 Session 过期时所做的操作,会自动退回到首页(例如:登录页面),以防止用户进行非法操作。
这件事情或许是每个具备安全性考虑的系统都需要的功能,我们可以分两种情况来处理用户的请求:
下面是具体的实现过程,您别忘了系好安全带,我们这就出发了!
第一步:在 Smart Framework 中定义一个认证异常类 AuthException
public class AuthException extends RuntimeException {
public AuthException() {
super();
}
public AuthException(String message) {
super(message);
}
public AuthException(String message, Throwable cause) {
super(message, cause);
}
public AuthException(Throwable cause) {
super(cause);
}
}
没啥内容,就一个普通的 RuntimeException 而已,其实就是想自定义一种异常类型,以区别于其他的异常,方便在框架中进行处理。
第二步:在 Smart Sample 中定义一个 AuthAspect,用于拦截 Action 的所有请求
@Bean
@Aspect(pkg = "com.smart.sample.action")
@Order(0)
public class AuthAspect extends BaseAspect {
@Override
public boolean filter(Class<?> cls, Method method, Object[] params) {
String className = cls.getSimpleName();
String methodName = method.getName();
return !(
className.equals("UserAction") &&
(methodName.equals("login") || methodName.equals("logout")
)
);
}
@Override
public void before(Class<?> cls, Method method, Object[] params) throws Exception {
User user = DataContext.Session.get("user");
if (user == null) {
throw new AuthException();
}
}
}
以上代码中需注意一下几点:
这样 AOP 框架(也就是 BaseAspect 类及其相关 Proxy Chain 等)就可以处理这个自定义异常了。
还记得 Order 注解吗?它曾经在单元测试中出现过,现在还可以用于定义 AOP 顺序。
友情提示:
第三步:在 BaseAspect 中将异常继续往上抛,抛给它的调用者
public abstract class BaseAspect implements Proxy {
@Override
public final void doProxy(ProxyChain proxyChain) throws Exception {
...
begin();
try {
if (filter(cls, method, params)) {
before(cls, method, params);
Object result = proxyChain.doProxyChain();
after(cls, method, params, result);
} else {
proxyChain.doProxyChain();
}
} catch (Exception e) {
error(cls, method, params, e);
throw e; // 将异常继续往上抛,抛给它的调用者
} finally {
end();
}
}
...
}
那么 Aspect 的调用者是谁呢?也就是说,谁用 Aspect 呢?至少 Action 是一个关键性用户。
那么 Action 又是谁来调用呢?当然就是 DispatchServlet 了。
于是,顺腾摸瓜,找到了调用的起源。
第四步:修改 DispatchServlet,处理 AuthException
@WebServlet("/*")
public class DispatcherServlet extends HttpServlet {
...
private void handleActionMethod(HttpServletRequest request, HttpServletResponse response, ActionBean actionBean, List<Object> paramList) {
// 从 ActionBean 中获取 Action 相关属性
Class<?> actionClass = actionBean.getActionClass();
Method actionMethod = actionBean.getActionMethod();
// 从 BeanHelper 中创建 Action 实例
Object actionInstance = BeanHelper.getInstance().getBean(actionClass);
// 调用 Action 方法
Object actionMethodResult;
try {
actionMethod.setAccessible(true); // 取消类型安全检测(可提高反射性能)
actionMethodResult = actionMethod.invoke(actionInstance, paramList.toArray());
} catch (Exception e) {
// 处理 Action 方法异常【★】
handleActionMethodException(request, response, e);
// 直接返回
return;
}
// 处理 Action 方法返回值
handleActionMethodReturn(request, response, actionMethodResult);
}
private void handleActionMethodException(HttpServletRequest request, HttpServletResponse response, Exception e) {
if (e.getCause() instanceof AuthException) {
// 若为认证异常,则分两种情况进行处理
if (WebUtil.isAJAX(request)) {
// 若为 AJAX 请求,则发送 403 错误
WebUtil.sendError(403, response);
} else {
// 否则重定向到首页
WebUtil.redirectRequest(request.getContextPath() + "/", response);
}
} else {
// 若为其他异常,则记录错误日志
logger.error("调用 Action 方法出错!", e);
}
}
...
}
大家可以看到以上代码的【★】处,也就是调用 Action 方法时的异常处理代码,请见下面的 handleActionMethodException 方法。逻辑如下:
以上代码中涉及到了几个关键的 WebUtil 方法,其实这些都是对 Servlet API 的一个简单的封装。代码片段如下:
public class WebUtil {
...
// 重定向请求
public static void redirectRequest(String path, HttpServletResponse response) {
try {
response.sendRedirect(path);
} catch (Exception e) {
logger.error("重定向请求出错!", e);
throw new RuntimeException(e);
}
}
// 发送错误代码
public static void sendError(int code, HttpServletResponse response) {
try {
response.sendError(code);
} catch (Exception e) {
logger.error("发送错误代码出错!", e);
throw new RuntimeException(e);
}
}
// 判断是否为 AJAX 请求
public static boolean isAJAX(HttpServletRequest request) {
return request.getHeader("X-Requested-With") != null;
}
}
以上可以看到,普通请求非常简单,直接 redirect 就行了。而对于 AJAX 请求回调问题,我们可使用 jQuery 来轻松实现。
第五步:使用 jQuery 来处理 AJAX 非法访问
/* 全局变量 */
var BASE = ‘/smart-sample‘; // 应用 Context 名称(若为空字符串表示应用以 ROOT 来发布)
...
$(function() {
$.ajaxSetup({
cache: false,
error: function(jqXHR, textStatus, errorThrown) {
switch (jqXHR.status) {
case 403:
document.write(‘‘);
location.href = BASE + ‘/‘;
break;
case 503:
alert(errorThrown);
break;
}
}
});
...
}
首先,定义一个全局变量 BASE,代表应用的 Context 名称,用户可自行修改。
然后,进行 AJAX 全局设置(使用了 jQuery 的 $.ajaxSetup 方法),在 error 回调函数中处理 403 错误,故意清空页面内容,并返回到应用首页(效果与普通请求非法访问时相同)。
以上就是 Smart Framework 关于安全性控制的解决方案,能否使用更简单更高效的方式来实现?等候您的评论。
来源:资阳网站建设
原文:https://www.cnblogs.com/0591jb/p/13603725.html