JavaEE权限管理系统的搭建(四)--------使用拦截器实现登录认证和apache shiro密码加密
RBAC
基于角色的权限访问控制(Role-Based Access Control)在RBAC中,权限与角色相关联,用户通过成为适当角色的成员而得到这些角色的权限。这就极大地简化了权限的管理。在一个组织中,角色是为了完成各种工作而创造,用户则依据它的责任和资格来被指派相应的角色,用户可以很容易地从一个角色被指派到另一个角色。角色可依新的需求和系统的合并而赋予新的权限,而权限也可根据需要而从某角色中回收。
在IEDA中,打开DATABASE菜单,连接数据库
一共5张表,很好理解,
用户和角色的关系:一个用户可以拥有多个角色,相反一个角色也可以被多个用户所拥有
角色和权限的关系:一个角色可以拥有多个权限,相反一个权限也可以被多个角色所拥有
建库和表的sql我这里就不贴了,好了,前期的配置到此就结束了,接下来下面正式进入开发之旅。。。。。。。。。。
//实现用户的登录--拦截认证--密码加密后验证登录
login.jsp
<%-- Created by IntelliJ IDEA. User: shaojiang Date: 2019/1/20 Time: 下午7:07 To change this template use File | Settings | File Templates. --%> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <!DOCTYPE HTML> <html> <head> <meta charset="utf-8"> <meta name="renderer" content="webkit|ie-comp|ie-stand"> <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no" /> <meta http-equiv="Cache-Control" content="no-siteapp" /> <!--[if lt IE 9]> <script type="text/javascript" src="/statics/lib/html5shiv.js"></script> <script type="text/javascript" src="/statics/lib/respond.min.js"></script> <![endif]--> <link href="/statics/h-ui/css/H-ui.min.css" rel="stylesheet" type="text/css" /> <link href="/statics/h-ui.admin/css/H-ui.login.css" rel="stylesheet" type="text/css" /> <link href="/statics/h-ui.admin/css/style.css" rel="stylesheet" type="text/css" /> <link href="/statics/lib/Hui-iconfont/1.0.8/iconfont.css" rel="stylesheet" type="text/css" /> <!--[if IE 6]> <script type="text/javascript" src="/statics/lib/DD_belatedPNG_0.0.8a-min.js" ></script> <script>DD_belatedPNG.fix('*');</script> <![endif]--> <title>JavaEE权限管理系统</title> <meta name="keywords" content="H-ui.admin v3.1,H-ui网站后台模版,后台模版下载,后台管理系统模版,HTML后台模版下载"> <meta name="description" content="H-ui.admin v3.1,是一款由国人开发的轻量级扁平化网站后台模板,完全免费开源的网站后台管理系统模版,适合中小型CMS后台系统。"> </head> <body> <input type="hidden" id="TenantId" name="TenantId" value="" /> <div class="header">JavaEE权限管理系统</div> <div class="loginWraper"> <div id="loginform" class="loginBox"> <form id="login_form1" class="form form-horizontal" onsubmit="return false" action="##" method="post"> <div style="color: red;text-align: center;">${requestScope.loginError}</div> <div class="row cl"> <label class="form-label col-xs-3"><i class="Hui-iconfont"></i></label> <div class="formControls col-xs-8"> <input id="userName" name="userName" type="text" placeholder="账户" class="input-text size-L"> </div> </div> <div class="row cl"> <label class="form-label col-xs-3"><i class="Hui-iconfont"></i></label> <div class="formControls col-xs-8"> <input id="password" name="password" type="password" placeholder="密码" class="input-text size-L"> </div> </div> <%--<div class="row cl">--%> <%--<div class="formControls col-xs-8 col-xs-offset-3">--%> <%--<input class="input-text size-L" type="text" placeholder="验证码" onblur="if(this.value==''){this.value='验证码:'}" onclick="if(this.value=='验证码:'){this.value='';}" value="验证码:" style="width:150px;">--%> <%--<img src=""> <a id="kanbuq" href="javascript:;">看不清,换一张</a> </div>--%> <%--</div>--%> <div class="row cl"> <div class="formControls col-xs-8 col-xs-offset-3"> <label for="rememberMe"> <input type="checkbox" name="rememberMe" id="rememberMe" value=""> 使我保持登录状态</label> </div> </div> <div class="row cl"> <div class="formControls col-xs-8 col-xs-offset-3"> <input id="sub_login" name="" type="button" class="btn btn-success radius size-L" value=" 登 录 "> <%--<input name="" type="reset" class="btn btn-default radius size-L" value=" 取 消 ">--%> </div> </div> </form> </div> </div> <div class="footer">Copyright 通用权限管理系统 by H-ui.admin v3.1</div> <script type="text/javascript" src="/statics/lib/jquery/1.9.1/jquery.min.js"></script> <script type="text/javascript" src="/statics/h-ui/js/H-ui.min.js"></script> <script type="text/javascript" src="/statics/lib/layer/2.4/layer.js"></script> <script type="text/javascript"> //当登录超时,会跳转到登录页面,如果在框架中加载了登录页,首先进行判断,如果不是顶级路径,就导致顶级路径 if (window.top!=window.self) { //alert("执行了"); window.top.location.href = '/admin/login'; } $(function() { // 读取cookies中的用户信息 var rem = $.cookie('rememberMe'); if(rem){ $("#rememberMe").prop("checked",true); $("#userName").val($.cookie("username")); $("#password").val($.cookie("password")); } //登录 $("#sub_login").click(function () { var username = $("#userName").val(); var password = $("#password").val(); if(!username){ layer.msg("请输入用户名"); $("#userName").focus().select(); return false; }else if(!password){ layer.msg("请输入登录密码"); $("#password").focus().select(); return false; }else { //验证是否需要保存账户信息到cookies saveCookies(); //alert($('#login_form1').serialize()); //登录系统 $.ajax({ //几个参数需要注意一下 type: "POST",//方法类型 dataType: "json",//预期服务器返回的数据类型 url: "/admin/login" ,//url data: $('#login_form1').serialize(), success: function (data) { console.log(data.result);//打印服务端返回的数据(调试用) if (data.result == 'SUCCESS'||data.result == 200) { window.location.href = "/admin/main"; }else { layer.msg("登录失败:"+data.result.toString(),{icon:5,time:2000}); } }, error : function() { layer.msg(data.result.toString()); } }); } }); }); /** * jquery.cookie.js 是一个轻量级的cookie 插件,可以读取、写入、删除 cookie。 * H-ui.js 中已封装jquery.cookie.js,无需单独下载。 */ function saveCookies() { if($("#rememberMe").is(":checked")){ //alert("记住我"); var username = $("#userName").val(); var password = $("#password").val(); //创建一个cookie并设置有效时间为 7天: //如果没有指定有效期,所创建的cookie有效期默认到用户关闭浏览器为止 $.cookie("rememberMe","true",{expires:7}); $.cookie("username",username,{expires:7 }); $.cookie("password",password,{expires:7 }); }else{ $.cookie("rememberMe","false",{expires:-1}); $.cookie("username","",{ expires:-1 }); $.cookie("password","",{ expires:-1 }); } } </script> </body> </html>
AdminController
/** * 管理员登录 * @return */ @RequestMapping(value = "/login",method = RequestMethod.POST) @ResponseBody public Map<String,Object> adminLogin( HttpServletRequest request, HttpSession session) { Map<String,Object> resultMap = new HashMap<String,Object>(); if(request.getParameter("userName")==null|| request.getParameter("password")==null){ resultMap.put("result","用户名或密码不能为空"); } //获取用户名和密码 String userName = request.getParameter("userName").toString(); String password = request.getParameter("password").toString(); //查询数据库该用户是否存在 AdminUser adminUser = adminUserService.findAdminUserByLoginName(userName); if(adminUser!=null){ //logger.info("adminUser:"+adminUser.toString()); //将用户的密码进行加密后返回,再进行比对 String encryptPassword = passwordEncryption.encryption(password, adminUser.getLoginName().toString()); if(adminUser.getLoginName().equals(userName)&& adminUser.getPassword().equals(encryptPassword)) { if(adminUser.getState()==1){ //验证通过,保存登录信息到session,转发到管理员后台控制器 session.setAttribute("adminUser",adminUser); resultMap.put("result","SUCCESS"); }else { resultMap.put("result","账户异常!请联系系统管理员!"); } }else { resultMap.put("result","用户名或密码错误!"); } }else { resultMap.put("result","用户名不存在!"); } return resultMap; }
登录时用到了passwordEncryption服务类,该类负责进行密码的加密,当用户登录时,将用户的密码进行加密,这里加密的规则,是把当前用户名作为盐值,使用sha-256算法,散列次数5,进行加密,加密后,再使用Base64进行编码,加密后根据当前登录的用户名查询该用户的数据库储存的加密密码比对是否一致,如果一致,登录成功。
使用自动注入引用
@Autowired
private PasswordEncryption passwordEncryption;
我是放在了service子模块中的,这样子方便扫描
PasswordEncryption类:
package com.supin51.service.impl; import org.apache.shiro.crypto.hash.SimpleHash; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import java.io.Serializable; /** * @Author:ShaoJiang * @description: apache shiro 的SimpleHash工具类加密 * @Date: created in 上午10:57 2019/1/26 * @Modified By: */ @Service public class PasswordEncryption{ /** * 基于注解的装配,这样可以在encrypt.properties属性文件中更改配置,在这里使用@Value进行注解装配获取到值
* 后期如果需要更改加密类型,可在属性文件中更改其他算法:md5,sha1,sha-128,sha-256,sha-512 * 使用@Value的类,在spring中,不能直接通过new 操作符来使用,这样获取不到值的 * 而是在需要使用的地方,通过spring的注解 @Autowired 来使用, * 例如:@Autowired private PasswordEncryption passwordEncryption; */ @Value("${algorithmName}") private String algorithmName;//加密算法,md5,sha1,sha-128,sha-256,sha-512 @Value("${hashIterations}") private int hashIterations;//散列次数 @Value("${isHex}") private boolean isHex;//是否使用十六进制编码,设置false后将启用Base64编码 /** * 密码加密 * @param password 加密前的密码 * @param salt 盐值(传递当前的用户名用作盐值) * @return */ public String encryption(String password, String salt) { String encryptStr = ""; if(isHex) { //使用十六进制进行编码 //带salt(盐值)和散列加密后,再用十六进制编码 encryptStr = new SimpleHash(algorithmName,password,salt,hashIterations).toHex(); //System.out.print("十六进制编码"); }else { //使用Base64进行编码 //带salt(盐值)和散列加密后,再用Base64编码 encryptStr = new SimpleHash(algorithmName,password,salt,hashIterations).toBase64(); //System.out.print("Base64编码"); } return encryptStr; } }
应为拦截了所有路径,所以还要添加认证拦截器:
AuthenticationInterceptor类:
package com.supin51.interceptor; import com.supin51.domain.AdminUser; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * @Author:ShaoJiang * @description:登录认证拦截 * @Date: created in 下午8:18 2019/1/21 * @Modified By: */ public class AuthenticationInterceptor implements HandlerInterceptor { private static final Log logger = LogFactory.getLog(AuthenticationInterceptor.class); //定义不需要拦截的URL路径 private static final String[] NO_INTERCEPTOR_URL = {"/admin/login","/404","/favicon.ico"}; /* * return true 才会继续执行下列两个方法(请求继续),否则整个请求结束 * 该方法主要进行拦截处理,该方法在Controller处理之前调用, * */ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { boolean flag = false; //获取请求的路径进行判断 String servletUrl = request.getServletPath(); logger.info("url认证拦截器:preHandle-------->"+servletUrl); //循环拦截的URL路径,如果当前请求的URL中包含需要拦截的URL,返回true for (String s :NO_INTERCEPTOR_URL) { if(servletUrl.contains(s)){ flag = true; break; } } //拦截请求 if(!flag){ //获取session中的用户信息 AdminUser adminUser = (AdminUser)request.getSession().getAttribute("adminUser"); //如果用户已登录,放行,否则拦截 if(adminUser!=null){ logger.info("url认证拦截器:preHandle-------->用户已登录,放行请求-------->"); flag = true; }else { logger.info("url认证拦截器:preHandle-------->用户未登录,拦截请求-------->"); request.setAttribute("message","登录信息失效,请重新登录"); //只适合跳转页面,URL地址保持不变 //request.getRequestDispatcher("user1/login").forward(request,response); response.sendRedirect("/admin/login");//重定向到控制器,URL地址改变 } } return flag; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { logger.info("url认证拦截器:postHandle-------->"); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { logger.info("url认证拦截器:afterCompletion-------->"); } }
至此,整个登录的实现流程结束。本小节结束,下一小节将实现,登录成功后的权限菜单显示,基于自定义注解方法级别的权限菜单的拦截验证。