思路:先創建一個專門的類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"); } }