基于URL的权限管理(三)
思路:先创建一个专门的类ActiveUser用于存储用户登录的信息,主要用于存储用户id,账户,名称,菜单,权限。
认证拦截器主要是查看用户是否已登陆,如果没有转发到登陆界面,用户用账户跟密码登录时候先验证账户密码认证。
如果正确登陆之后进入授权拦截器中,授权拦截器主要查看session域中用户的菜单与权限(如果其权限满足能访问资源就放行)。用户每点一次按钮都会访问一个URL,都会进行权限的判断。
在session中存取用户信息的思路:根据用户的id去关联查询用户的菜单与权限以对象形式放入list中,并将查出来的信息存到ActiveUser,将ActiveUser存到session域中。菜单主要是进去之后左边的大菜单,菜单的URL是点击大菜单的时候跳转的页面,权限是其点击小按钮进行每一次访问的URL(也会进行处理),这样处理比较严谨。
1.流程
2.系统登陆
系统 登陆相当 于用户身份认证,用户成功,要在session中记录用户的身份信息.
操作流程:
用户进行登陆页面
输入用户名和密码进行登陆
进行用户名和密码校验
如果校验通过,在session记录用户身份信息
2.1 用户的身份信息
创建专门类用于记录用户身份信息。
package cn.qlq.springmvc.pojo; import java.util.Iterator; import java.util.List; import com.sun.org.apache.bcel.internal.generic.NEW; /** * 用户身份信息,存入session 由于tomcat将session会序列化在本地硬盘上,所以使用Serializable接口 * * @author Thinkpad * */ public class ActiveUser implements java.io.Serializable { private String userid;//用户id(主键) private String usercode;// 用户账号 private String username;// 用户名称 private List<SysPermission> menus;// 菜单 private List<SysPermission> permissions;// 权限 public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getUsercode() { return usercode; } public void setUsercode(String usercode) { this.usercode = usercode; } public String getUserid() { return userid; } public void setUserid(String userid) { this.userid = userid; } public List<SysPermission> getMenus() { return menus; } public void setMenus(List<SysPermission> menus) { this.menus = menus; } public List<SysPermission> getPermissions() { return permissions; } public void setPermissions(List<SysPermission> permissions) { this.permissions = permissions; } }
2.2 mapper
mapper接口: 根据用户账号查询用户(sys_user)信息(使用逆向工程生成的mapper)
查询菜单与权限的时候需要进行多表查询,自定义mapper
mapper.java
public interface SysPermissionMapperCustom { //根据用户id查询菜单 public List<SysPermission> findMenuListByUserId(String userid)throws Exception; //根据用户id查询权限url public List<SysPermission> findPermissionListByUserId(String userid)throws Exception; }
mapper.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > <mapper namespace="cn.qlq.springmvc.mapper.SysPermissionMapperCustom" > <!-- 根据用户id查询url --> <select id="findPermissionListByUserId" parameterType="string" resultType="cn.qlq.springmvc.pojo.SysPermission"> SELECT * FROM sys_permission WHERE TYPE = 'permission' AND id IN (SELECT sys_permission_id FROM sys_role_permission WHERE sys_role_id IN (SELECT sys_role_id FROM sys_user_role WHERE sys_user_id = #{id})) </select> <!-- 根据用户id查询菜单 --> <select id="findMenuListByUserId" parameterType="string" resultType="cn.qlq.springmvc.pojo.SysPermission"> SELECT * FROM sys_permission WHERE TYPE = 'menu' AND id IN (SELECT sys_permission_id FROM sys_role_permission WHERE sys_role_id IN (SELECT sys_role_id FROM sys_user_role WHERE sys_user_id = #{id})) </select> </mapper>
2.3 Service(进行用户名和密码校验)
接口功能:根据用户的身份和密码 进行认证,如果认证通过,返回用户身份信息
认证过程:
根据用户身份(账号)查询数据库,如果查询不到用户不存在
对输入的密码 和数据库密码 进行比对,如果一致,认证通过
package cn.qlq.springmvc.serviceImpl; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import cn.qlq.springmvc.Exception.MyException; import cn.qlq.springmvc.mapper.SysPermissionMapperCustom; import cn.qlq.springmvc.mapper.SysUserMapper; import cn.qlq.springmvc.pojo.ActiveUser; import cn.qlq.springmvc.pojo.SysPermission; import cn.qlq.springmvc.pojo.SysUser; import cn.qlq.springmvc.pojo.SysUserExample; import cn.qlq.springmvc.service.SysService; import cn.qlq.springmvc.utils.MD5; /** * 认证和授权 * * @author: qlq * @date : 2017年7月28日上午10:45:52 */ @Service public class SysServiceImpl implements SysService { @Autowired private SysUserMapper sysUserMapper; @Autowired private SysPermissionMapperCustom sysPermissionMapperCustom; @Override public ActiveUser authenticat(String userCode, String password) throws Exception { /** * 认证过程: 根据用户身份(账号)查询数据库,如果查询不到用户不存在 对输入的密码 和数据库密码 进行比对,如果一致,认证通过 */ // 根据用户账号查询数据库 SysUser sysUser = this.findSysUserByUserCode(userCode); if (sysUser == null) { // 抛出异常 throw new MyException("用户账号不存在"); } // 数据库密码 (md5密码 ) String password_db = sysUser.getPassword(); // 对输入的密码 和数据库密码 进行比对,如果一致,认证通过 // 对页面输入的密码 进行md5加密 String password_input_md5 = new MD5().getMD5ofStr(password); if (!password_input_md5.equalsIgnoreCase(password_db)) { // 抛出异常 throw new MyException("用户名或密码 错误"); } // 得到用户id String userid = sysUser.getId(); // 根据用户id查询菜单 List<SysPermission> menus = this.findMenuListByUserId(userid); // 根据用户id查询权限url List<SysPermission> permissions = this.findPermissionListByUserId(userid); // 认证通过,返回用户身份信息 ActiveUser activeUser = new ActiveUser(); activeUser.setUserid(sysUser.getId()); activeUser.setUsercode(userCode); activeUser.setUsername(sysUser.getUsername());// 用户名称 // 放入权限范围的菜单和url activeUser.setMenus(menus); activeUser.setPermissions(permissions); return activeUser; } // 根据用户账号查询用户信息 public SysUser findSysUserByUserCode(String userCode) throws Exception { // 根据用户名查询用户信息 SysUserExample sysUserExample = new SysUserExample(); SysUserExample.Criteria criteria = sysUserExample.createCriteria(); criteria.andUsercodeEqualTo(userCode); List<SysUser> list = sysUserMapper.selectByExample(sysUserExample); if (list != null && list.size() == 1) { return list.get(0); } return null; } @Override public List<SysPermission> findMenuListByUserId(String userid) throws Exception { return sysPermissionMapperCustom.findMenuListByUserId(userid); } @Override public List<SysPermission> findPermissionListByUserId(String userid) throws Exception { return sysPermissionMapperCustom.findPermissionListByUserId(userid); } }
2.4 controller(记录session)
package cn.qlq.springmvc.controller; import javax.servlet.http.HttpSession; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import cn.qlq.springmvc.Exception.MyException; import cn.qlq.springmvc.pojo.ActiveUser; import cn.qlq.springmvc.service.SysService; @Controller public class LoginController { @Autowired private SysService sysService; //用户登陆提交方法 /** * * <p>Title: login</p> * <p>Description: </p> * @param session * @param randomcode 输入的验证码 * @param usercode 用户账号 * @param password 用户密码 * @return * @throws Exception */ @RequestMapping("/login") public String login(HttpSession session, String randomcode,String usercode,String password)throws Exception{ //校验验证码,防止恶性攻击 //从session获取正确验证码 String validateCode = (String) session.getAttribute("validateCode"); //输入的验证和session中的验证进行对比 if(!randomcode.equals(validateCode)){ //抛出异常 throw new MyException("验证码输入错误"); } //调用service校验用户账号和密码的正确性 ActiveUser activeUser = sysService.authenticat(usercode, password); //如果service校验通过,将用户身份记录到session session.setAttribute("activeUser", activeUser); //重定向到商品查询页面 return "redirect:/first.action"; } //用户退出 @RequestMapping("/logout") public String logout(HttpSession session)throws Exception{ //session失效 session.invalidate(); //重定向到商品查询页面 return "redirect:/first.action"; } }
2.5 用户认证拦截器
package cn.qlq.springmvc.inteceptor; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; /** * SpringMVC的拦截器 * @author: qlq * @date : 2017年7月22日下午12:20:52 */ public class Inteceptor1 implements HandlerInterceptor{ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object arg2) throws Exception { // 判断是否是登陆 String requestURI = request.getRequestURI(); //springmvc-mybatis/itemlist.action StringBuffer requestURL = request.getRequestURL(); //http://localhost:8080/springmvc-mybatis/itemlist.action // 判断用户是否登陆 如果没有登陆 重定向到登陆页面 不放行 如果登陆了 就放行了 if(!requestURI.contains("login")){ Object attribute = request.getSession().getAttribute("user"); if(attribute==null){ response.sendRedirect(request.getContextPath()+"/login.action"); return false; } } //不放行的话返回false return true; } @Override public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3) throws Exception { System.out.println("这是方法后1"); } @Override public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3) throws Exception { // TODO Auto-generated method stub System.out.println("这是页面渲染后1"); } }
2.6用户授权拦截器(在认证拦截器之后配置)
package cn.qlq.springmvc.inteceptor; import java.util.List; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import cn.qlq.springmvc.pojo.ActiveUser; import cn.qlq.springmvc.pojo.SysPermission; import cn.qlq.springmvc.utils.ResourcesUtil; public class PermissionInterceptor implements HandlerInterceptor { //在执行handler之前来执行的 //用于用户认证校验、用户权限校验 @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { //得到请求的url String url = request.getRequestURI(); //判断是否是公开 地址 //实际开发中需要公开 地址配置在配置文件中 //从配置中取逆名访问url List<String> open_urls = ResourcesUtil.gekeyList("anonymousURL"); //遍历公开 地址,如果是公开 地址则放行 for(String open_url:open_urls){ if(url.indexOf(open_url)>=0){ //如果是公开 地址则放行 return true; } } //从配置文件中获取公共访问地址 List<String> common_urls = ResourcesUtil.gekeyList("commonURL"); //遍历公用 地址,如果是公用 地址则放行 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> permissions = activeUser.getPermissions(); for(SysPermission sysPermission:permissions){ //权限的url String permission_url = sysPermission.getUrl(); if(url.indexOf(permission_url)>=0){ //如果是权限的url 地址则放行 return true; } } //执行到这里拦截,跳转到无权访问的提示页面 request.getRequestDispatcher("/WEB-INF/jsp/refuse.jsp").forward(request, response); //如果返回false表示拦截不继续执行handler,如果返回true表示放行 return false; } //在执行handler返回modelAndView之前来执行 //如果需要向页面提供一些公用 的数据或配置一些视图信息,使用此方法实现 从modelAndView入手 @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("HandlerInterceptor1...postHandle"); } //执行handler之后执行此方法 //作系统 统一异常处理,进行方法执行性能监控,在preHandle中设置一个时间点,在afterCompletion设置一个时间,两个时间点的差就是执行时长 //实现 系统 统一日志记录 @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("HandlerInterceptor1...afterCompletion"); } }
源码地址:
https://github.com/qiao-zhi/quanxian.git
【当你用心写完每一篇博客之后,你会发现它比你用代码实现功能更有成就感!】