在使用shiro的时候,对于用户权限的管理,相信很多人都已经很熟悉了。
今天,我这里简单的记录一下我自己调试过程中遇到的问题。主要是登录的操作,禁止通过ajax的方式进行访问。

shiro中,登录过程拒绝ajax的访问操作,主要在FormAuthenticationFilter里面实现的。更具体的说,应该是自己重写AccessControlFilter方法中的onAccessDenied方法。

为什么要禁止ajax的登录?安全的考虑!

先看看自己应用中重写的RdFormAuthenticationFilter,它继承于FormAuthenticationFilter,至于FormAuthenticationFilter和AccessControlFilter的关系,自己看shiro的源码吧。

public class RdFormAuthenticationFilter extends FormAuthenticationFilter {

    private String passwordParam;

    @Autowired
@Qualifier("mqmKefuService")
private IMqmKefuService mksService; @Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response, Object mappedValue)
throws Exception {
if (request.getAttribute(getFailureKeyAttribute()) != null) {
return true;
} HttpServletRequest httpServletRequest = (HttpServletRequest)request;
if (isLoginSubmission(request, response)) {
return executeLogin(request, response);
} else {
if ("XMLHttpRequest".equalsIgnoreCase(httpServletRequest.getHeader("X-Requested-With"))
|| request.getParameter("ajax") != null) {
HttpServletResponse res = WebUtils.toHttp(response);
res.sendError(HttpServletResponse.SC_UNAUTHORIZED);
}
return true;
}
} @Override
protected boolean onLoginSuccess(AuthenticationToken token, Subject subject,
ServletRequest request, ServletResponse response) throws Exception {
HttpServletRequest httpServletRequest = (HttpServletRequest)request;
HttpServletResponse httpServletResponse = (HttpServletResponse)response; String username = (String)SecurityUtils.getSubject().getPrincipal();
if(username == null){
return true;
} MqmKefu user = mksService.selectByUsername(username); SecurityUtils.getSubject().getSession().setAttribute(Constants.CURRENT_USER, user); if ("XMLHttpRequest".equalsIgnoreCase(httpServletRequest.getHeader("X-Requested-With"))
|| request.getParameter("ajax") != null) {
// 是ajax请求
httpServletRequest.getRequestDispatcher("/login/timeout/success").forward(httpServletRequest, httpServletResponse);
} else {
//httpServletResponse.sendRedirect(httpServletRequest.getContextPath() + this.getSuccessUrl());
String redirectUrl = httpServletRequest.getContextPath() + "/home";
SavedRequest sr = WebUtils.getSavedRequest(request);
//当用户地址的请求不是在地址栏输入,而是在页面上直接点击登录,那么SavedRequest返回值将会是空的。
if(sr != null) {
redirectUrl = sr.getRequestUrl();
}
httpServletResponse.sendRedirect(redirectUrl);
} return false;
} public String getPasswordParam() {
return passwordParam;
} public void setPasswordParam(String passwordParam) {
this.passwordParam = passwordParam;
} }

AAAA。这里,重点是下面的这个代码片段:

if ("XMLHttpRequest".equalsIgnoreCase(httpServletRequest.getHeader("X-Requested-With"))
|| request.getParameter("ajax") != null) {
HttpServletResponse res = WebUtils.toHttp(response);
res.sendError(HttpServletResponse.SC_UNAUTHORIZED);
}

如何测试呢?很简单!
1. 将shiro的配置中,重写LogoutFilter,并将其redirectUrl的值配置为自己想要的内容,而不是原来默认的DEFAULT_REDIRECT_URL(值为/)。例如,将redirectUrl改为登录的url,这里是/usr/login.
2. 将前端页面的退出,一种用ajax的post或者get方式触发,一种用href直接配置url。
2.1 针对退出url (/usr/logout)以ajax的方式触发事件到后台,代码逻辑将会进入上述代码AAAA,且此时HTTP请求头部含有X-Requested-With。前端将收到401的错误,页面不跳转。
2.2 针对退出url以a标签的href方式,直接由浏览器以http调用后台服务的方式,则不会进入AAAA的代码逻辑。

针对上述的LogoutFilter的重写,可以看看配置文件:

    <bean id="sysUserFilter" class="com.roomdis.mqr.infra.shiro.SysUserFilter"/>
<bean id="sysLogoutFilter" class="com.roomdis.mqr.infra.shiro.RdLogoutFilter">
<property name="redirectUrl" value="/user/login"/>
</bean>
<bean id="jCaptchaValidateFilter" class="com.roomdis.mqr.infra.shiro.JCaptchaValidateFilter">
<property name="jcaptchaEbabled" value="true"/>
<property name="jcaptchaParam" value="jcaptchaCode"/>
<property name="failureKeyAttribute" value="shiroLoginFailure"/>
</bean> <bean id="filterChainManager" class="com.roomdis.mqr.infra.shiro.RdDefaultFilterChainManager">
<property name="loginUrl" value="/user/login" />
<property name="successUrl" value="/home" />
<property name="unauthorizedUrl" value="/unauth" />
<property name="customFilters">
<util:map>
<entry key="authc" value-ref="authcFilter"/>
<entry key="sysUser" value-ref="sysUserFilter"/>
<entry key="jCaptchaValidate" value-ref="jCaptchaValidateFilter"/>
<entry key="logout" value-ref="sysLogoutFilter" />
</util:map>
</property>
<property name="defaultFilterChainDefinitions">
<value>
/ = anon
/*.png = anon
/css/* = anon
/js/** = anon
/image/* = anon
/index.html = anon
/user/login = jCaptchaValidate,authc
/jcaptcha* = anon
/jcaptcha.jsp = anon
/home = sysUser,user
/logout = logout
<!-- /home = authc, perms[/home] perms 表示需要该权限才能访问的页面 -->
</value>
</property>
</bean>

上述配置的红色部分,是重写LogoutFilter的对应bean的信息。

蓝色部分,要特别注意,logout的url,权限部分处理,必须是和自己代码逻辑中的重定向部分一致使用。例如这里的/logout,对应代码逻辑中的地方(红色)如下:

    /**
* logout 即常说的登出操作
*
* @param req
* @param rsp
* @param model
* @return
*/
@GET
@Path("/user/logout")
@Produces(MediaType.APPLICATION_JSON)
public void logout(@Context HttpServletRequest req, @Context HttpServletResponse rsp){
Map<String, Object> infoMap = new HashMap<String, Object>();
String basePath = basePath(req);
String usernamep = req.getParameter("username");
infoMap.put("basePath", basePath);
SecurityUtils.getSubject().getSession().removeAttribute(Constants.CURRENT_USER);
try {
rsp.sendRedirect(req.getContextPath() + "/logout");
} catch (IOException e) {
e.printStackTrace();
}
}

前端的代码:

<p class="checkmove">
<span class="fl sp_title">
<#if username??>
<label id="username">${username}</label>
<a class=logout-btn href="${basePath}/user/logout">退出</a> #能够正常运行,即不会触发401的错误的用法
<!--
<button class=button id="leave">退出</button> #这样子用,配合后台的ajax的方式触发,就会出现401的错误
-->
</#if>
<select>
<option value="">上线</option>
<option value="">离开</option>
<option value="">隐身</option>
<option value="">下线</option>
</select>
</span>
</p>

下面上一个图,看看我的项目雏形!

最新文章

  1. Visual Studio Code中文文档(一)-快速入门
  2. c++ 之 编译期多态&amp;运行期多态
  3. js中原型继承的三种方式
  4. javascript中的arguments对象
  5. linux创建用户、设置密码、修改用户、删除用户
  6. Hibernate——property的access属性
  7. The sound of silence引发的关于互联网以及教育的利弊思考
  8. iOS - 文件操作(File Operating)
  9. Servlet 服务器端小程序
  10. for循环去重排序
  11. 浅谈Android布局
  12. 秒懂ASP.NET中的内置对象
  13. c#语言中的Process进程类型的使用示例
  14. iptables/mysql设置指定主机访问指定端口
  15. XBee模块户外通信距离测试
  16. IdentityServer4 中文文档 -13- (快速入门)切换到混合流并添加 API 访问
  17. Windows 10 如何使用「系统还原」功能备份系统状态和配置
  18. Vim命令图解及快捷键讲解
  19. DNS的过程
  20. 20155308 《网络攻防》 Exp2 后门原理与实践

热门文章

  1. python day08作业答案
  2. Python 数据结构--排序
  3. python3+requests:get/post请求
  4. Excel导入CSV文件中文乱码
  5. SQLAlchemy中表结构的一对一
  6. CF1132.Educational Codeforces Round 61(简单题解)
  7. PAT-L2-006(根据后序中序遍历建立树)
  8. day021python 正则表达式
  9. BFS广度优先搜索 炸弹人
  10. Mac下常用工具