基于RUL的权限管理
我想在写shiro权限管理认证前,先来一个基于URL实现的权限管理控制。
一、基于URI的权限业务逻辑
实现思路:
将系统操作的每个url配置在权限表中,将权限对应到角色,将角色分配给用户,用户访问系统功能通过Filter进行过虑,过虑器获取到用户访问的url,只要访问的url是用户分配角色中的url则放行继续访问。
具体逻辑看下面一张表。
二、环境搭建(数据表)
思维已经清晰,接下来通过代码来实现具体业务逻辑。
开发环境
JDK1.8
Eclipse
Maven
技术架构:SpringMVC+Mybatis+jQuery easyUI
数据库
MySQL数据库中创建表:用户表、角色表、权限表(实质是权限和资源的结合)、用户角色关系表、角色权限关系表。
(1)具体表
sys_user 用户表(这里明文密码都是:111111)
sys_role 角色表
sys_user_role 权限表
sys_permission 权限表
sys_role_permission 角色权限关系表
三、用户认证
1.login.action用户登录页面
<BODY > <FORM id="loginform" name="loginform" action="?loginsubmit.action" method="post"> <TABLE class="tab" border="0" cellSpacing="6" cellPadding="8"> <TBODY> <TR> <TD>用户名:</TD> <TD colSpan="2"><input type="text" id="usercode" name="usercode" style="WIDTH: 130px" /></TD> </TR> <TR> <TD>密 码:</TD> <TD><input type="password" id="pwd" name="password" style="WIDTH: 130px" /> </TD> </TR> <TR> <TD>验证码:</TD> <TD><input id="randomcode" name="randomcode" size="8" /> <img id="randomcode_img" src="${baseurl}validatecode.jsp" alt="" width="56" height="20" align='absMiddle' /> <a href=javascript:randomcode_refresh()>刷新</a></TD> </TR> <TR> <TD colSpan="2" align="center"><input type="sumbit" value="登 录" /> </TD> </TR> </TBODY> </TABLE> </FORM> </BODY>
2、SpringMVC配置过滤器
<!-- 用户身份校验的拦截器 --> <mvc:interceptor> <mvc:mapping path="/**" /> <bean class="com.jincou.controller.interceptor.LoginInterceptor"></bean> </mvc:interceptor>
3、LoginInterceptor过滤器
public class LoginInterceptor implements HandlerInterceptor { // 在进入controller方法之前执行 // 进行身份认证校验拦截,如果拦截不放行,controller方法不再执行 @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 校验用户访问是否是公开资源地址(无需认证即可访问) List<String> open_urls = ResourcesUtil.gekeyList("anonymousURL"); // 用户访问的url(这里用户登录页面login.action,和登录提交页面loginsubmit.action属于公开访问页面,直接发行) String url = request.getRequestURI(); for (String open_url : open_urls) { if (url.indexOf(open_url) >= 0) { // 如果访问的是公开 地址则放行 return true; } } // 校验用户身份是否认证通过 HttpSession session = request.getSession(); ActiveUser activeUser = (ActiveUser) session.getAttribute("activeUser"); if (activeUser != null) { // 用户已经登陆认证,放行 return true; } // 跳转到登陆页面 request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request, response); return false; } }
(1)anonymousURL.properties配置文件
#不需要登录就可以访问的页面:比如登录页面,登陆提交的地址,网站首页等 login.action=登录页面 loginsubmit.action=登陆提交按钮的地址
4、LoginControl控制层
/** * 获得用户名,密码,验证码进行用户验证,验证通过就把该用户放到session中 */ @Controller public class LoginController { @Autowired private SysService sysService ; //用户登陆提交 @RequestMapping("/loginsubmit") public String loginsubmit(HttpSession session,String usercode,String password,String randomcode) throws Exception{ //校验验证码 //从session获取正确的验证码(因为验证码是从前端生成,传到后端) String validateCode = (String)session.getAttribute("validateCode"); if(!randomcode.equals(validateCode)){ //抛出异常:验证码错误 throw new CustomException("验证码 错误 !"); } //用户身份认证 ActiveUser activeUser = sysService.authenticat(usercode, password); //记录session session.setAttribute("activeUser", activeUser); return "redirect:first.action"; } }
4.1ActiveUser实体类
/** * 用户身份信息,获取通过用户名得到密码和菜单和页面权限 */ public class ActiveUser implements java.io.Serializable { /** * */ private static final long serialVersionUID = 1L; private String userid;//用户id private String usercode;// 用户账号 private String username;// 用户名称 private List<SysPermission> menus;// 菜单 private List<SysPermission> permissions;// 权限 //set和get方法 }
5.SysServiceImpl实现类
//@Override public ActiveUser authenticat(String usercode, String password) throws Exception { //账号和密码非空校验 //.... SysUserExample sysUserExample = new SysUserExample(); SysUserExample.Criteria criteria = sysUserExample.createCriteria(); criteria.andUsercodeEqualTo(usercode); List<SysUser> userList = sysUserMapper.selectByExample(sysUserExample); if(userList == null || userList.size()<=0){ throw new CustomException("账号不存在!"); } SysUser sysUser = userList.get(0); //密码 String password_fromdb = sysUser.getPassword(); //输入 密码 和数据库密码 比较(因为数据库加过密所以这里也加密后进行比较) if(!password_fromdb.equalsIgnoreCase(new MD5().getMD5ofStr(password))){ throw new CustomException("账号或密码 错误 !"); } //认证通过,返回用户身份 ActiveUser activeUser = new ActiveUser(); activeUser.setUserid(sysUser.getId()); activeUser.setUsername(sysUser.getUsername()); activeUser.setUsercode(sysUser.getUsercode()); //菜单列表 List<SysPermission> menus = sysPermissionMapperCustom.findMenuByUserid(sysUser.getId()); activeUser.setMenus(menus); //权限列表 List<SysPermission> permissions = sysPermissionMapperCustom.findPermissionByUserid(sysUser.getId()); activeUser.setPermissions(permissions); //获得用户菜单和权限列表 return activeUser; }
总结:
用户认证的思路很简单:
(1)判断用户登录是否成功,成功将用户信息放入session中,登录后点击其它页面,过滤器会看session是否存在用户,存在则放行。
(2)如果你没有登录,点击其它非公共页面,那过滤器会发现session中没有用户信息,则跳转到登录页面。
三、权限管理
既你登录认证通过后,根据用户id从数据库中获取用户权限范围的URL,将URL的集合存储在activeUser中并存在session中。
1.springMVC配置权限认证过滤器
<!-- 用户登陆成功后的资源权限拦截器 --> <mvc:interceptor> <mvc:mapping path="/**" /> <bean class="com.jincou.controller.interceptor.PermissionInterceptor"></bean> </mvc:interceptor>
2、PermissionInterceptor过滤器
public class PermissionInterceptor implements HandlerInterceptor { // 在进入controller方法之前执行 // 用户权限拦截,如果拦截不放行,controller方法不再执行 // 进入action方法前要执行 @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // TODO Auto-generated method stub // 用户访问地址: String url = request.getRequestURI(); // 校验用户访问是否是公开资源地址(无需认证即可访问) List<String> open_urls = ResourcesUtil.gekeyList("anonymousURL"); // 用户访问的url for (String open_url : open_urls) { if (url.indexOf(open_url) >= 0) { // 如果访问的是公开 地址则放行 return true; } } //从 session获取用户公共访问地址(认证通过无需分配权限即可访问) List<String> common_urls = ResourcesUtil.gekeyList("commonURL"); // 用户访问的url for (String common_url : common_urls) { if (url.indexOf(common_url) >= 0) { // 如果访问的是公共地址则放行 return true; } } // 从session获取用户权限信息 HttpSession session = request.getSession(); ActiveUser activeUser = (ActiveUser) session.getAttribute("activeUser"); // 取出session中权限url // 获取用户操作权限 List<SysPermission> permission_list = activeUser.getPermissions(); // 校验用户访问地址是否在用户权限范围内 for (SysPermission sysPermission : permission_list) { String permission_url = sysPermission.getUrl(); if (url.contains(permission_url)) { return true; } } // 跳转到无权访问页面 request.getRequestDispatcher("/refuse.jsp").forward( request, response); return false; } }
(1)commonURL配置文件
#用户公共访问地址指的是用户登录后,有些页面也是不需要配置权限的,每个用户都会有的页面:比如:个人中心,退出页面等 first.action=系统页面 logout.action=退出页面
总结:
权限认证的思路:
(1)当用户登录成功后,因为上面ActiveUser对象,里面有用户可以访问哪些菜单,所以页面也只会显示这些菜单。
(2)当我用户浏览我没有权限的URL页面,那么PermissionInterceptor会判断我没有这个页面的权限,直接调整到无权访问页面。
四、一些细节
1、前段页面是如何做到当前用户可以获得哪些菜单(可以循环遍历)
<c:if test="${activeUser.menus!=null }"> <c:forEach items="${activeUser.menus }" var="menu"> <a href="${pageContext.request.contextPath }/${menu.url }" >${menu.name }</a> </c:forEach> </c:if>
2、验证下"zhangsan"用户能看到的菜单是否正确
(1)先看没有做权限页面的菜单
(2)再看做了权限管理的页面
(3)那到底是不是这样?(数据查询正确)
github地址: https://github.com/yudiandemingzi/URLquanxian
想太多,做太少,中间的落差就是烦恼。想没有烦恼,要么别想,要么多做。少校【3】