• 基于SSM + Redis的Shiro权限管理项目


    概述

    本教程结合SSM(SpringMVC + Mybatis)框架讲解Shiro,讲解的内容有自定义shiro拦截器,Shiro Freemarker标签,Shiro JSP标签,权限控制讲解。

    详细

    一、项目介绍(准备工作)

    运行前申明

    1. 请看完本页面的所有细节,对你掌握这个项目来说很重要,别一上来就搞,你不爽,我也不爽。

    2. 本项目需要一定的Java功底,需要对SpringMvcMybatis,有基本的了解,其次对Redis有了解和使用更佳。

    3. 本项目理论上,只需要一个Redis,然后一个Mysql和一个有Maven环境的开发工具即可运行起来。

    4. 对Reids没有了解,请看这里:对Redis的理解,Redis是什么,Redis和Memcache谁快?

    运行步骤

    1. 下载源码,导入到EclipseMyEclipesIdea类似开发工具。

    2. 解决编译错误,修改JDK1.7以上(请勿使用工具自带JDK)。

    3. Mysql数据库中创建一个数据库,库名随便,数据库版本为5.6(必要条件,否则部分语法不支持)。

    4. 从项目/init/sql/下,先执行tables.sql创建表,再运行init.data.sql插入初始化数据。

    5. 再修改配置jdbc.properties把数据库链接改成您的。

    6. 安装Redis服务,如果您没用过,或者没安装,请看这里==>Redis 安装,以及注意事项都在里面有说明。Redis启动报错请看这里:Please see the documentation included with the binary distributions for more details on the --maxheap flag.

    7. 安装完毕后,修改配置:spring-cache.xml,如果是本地,无序修改,启动Redis,如对Redis不了解的同学,建议别设置密码。

    二、运行效果

    1. 项目帐号和线上Demo一致:管理员帐号:admin,密码:sojson.com 如果密码错误,请用 sojson

    2. 线上 Demo:http://shiro.itboy.net/

    3. 截图如下:

      image.png

    三、程序实现过程

    1、SSM(SpringMVC + Mybatis)框架的增删改查(含分页)

    本教程是SSM(SpringMVC + Spring + Mybatis + Freemarker + JSP) + Shiro + Redis 做的整体Demo,其他框架需要自己自行解决,所以不做其他框架的讲解,其实是大同小异。

    Controller ==> Service(事务控制层) ==> Dao ==> SqlMapper ==> Mysql

    2、View层 Freemarker,JSP

    通用View层配置在spring-mvc.xml中的以【通用试图解析器】注释标注的区间配置。

    3、Shiro + Redis 的集成,也提供Ehcache的依赖Jar

    Redis 缓存配置主要在spring-cache.xml中。对应的所有Cache 相关 Java 代码在package:com.sojson.core.shiro.cache

    4、Shiro 初始权限动态加载

    我们一般是这么加载的。在spring-shiro.xml中配置

    <property name="filterChainDefinitions" >
    	<value>
    		/** = anon
    		/page/login.jsp = anon
    		/page/register/* = anon
    		/page/index.jsp = authc
    		/page/addItem* = authc,roles[数据管理员]
    		/page/file* = authc,roleOR[普通用户,数据管理员]
    		/page/listItems* = authc,roleOR[数据管理员,普通用户]
    		/page/showItem* = authc,roleOR[数据管理员,普通用户]
    		/page/updateItem*=authc,roles[数据管理员]
              </value>
    </property>

    本教程采用动态加载,你可以从数据库里读取然后拼接成shiro要的数据。

    <property name="filterChainDefinitions" value="#{shiroManager.loadFilterChainDefinitions()}"/>

    配置文件方式加载详细讲解:http://www.sojson.com/blog/148.html

    5、Shiro 自定义权限校验Filter定义,及功能实现

    Shiro Filter在package:com.sojson.core.shiro.filter,具体配置在spring-shiro.xml中。定义了5个拦截器,具体功能看代码以及代码注释。

    <bean id="shiroManager" class="com.sojson.core.shiro.service.impl.ShiroManagerImpl"/>
    <bean id="login" class="com.sojson.core.shiro.filter.LoginFilter"/>
    <bean id="role" class="com.sojson.core.shiro.filter.RoleFilter"/>
    <bean id="permission" class="com.sojson.core.shiro.filter.PermissionFilter"/>
    <bean id="simple" class="com.sojson.core.shiro.filter.SimpleAuthFilter"/>
    <property name="filters">
       <util:map>
    	  <entry key="login" value-ref="login"></entry>
    	  <entry key="role" value-ref="role"></entry>
    	  <entry key="simple" value-ref="simple"></entry>
    	  <entry key="permission" value-ref="permission"></entry>
       </util:map>
    </property>	

    6、Shiro Ajax请求权限不满足,拦截后解决方案

    这里有一个前提,我们知道Ajax不能做页面redirectforward跳转,所以Ajax请求假如没登录,那么这个请求给用户的感觉就是没有任何反应,而用户又不知道用户已经退出了。解决代码如下:

    //Java代码,判断如果是Ajax请求,然后并且没登录,那么就给予返回JSON,login_status = 300,message = 当前用户没有登录!
    if (ShiroFilterUtils.isAjax(request)) {// ajax请求
    	Map<String,String> resultMap = new HashMap<String, String>();
    	LoggerUtils.debug(getClass(), "当前用户没有登录,并且是Ajax请求!");
    	resultMap.put("login_status", "300");
    	resultMap.put("message", "u5F53u524Du7528u6237u6CA1u6709u767Bu5F55uFF01");//当前用户没有登录!
    	ShiroFilterUtils.out(response, resultMap);
    }
    //前端代码
    if(result.login_status == 300){
    	layer.msg(result.message);//当前用户没有登录!
    }

    7、Shiro Freemarker标签使用

    Freemarker使用Shiro 标签的介绍:http://www.sojson.com/blog/143.html

    8、Shiro JSP标签使用

    JSP使用Shiro 标签的介绍:http://www.sojson.com/blog/144.html

    9、Shiro 登录后跳转到最后一个访问的页面

     Java 中就可以这样获取上一个地址:

    //上一个浏览的非Ajax的地址,在登录后,取得地址,如果不为null,那么就跳转过去。
    String url = (String) request.getAttribute(WebUtils.FORWARD_REQUEST_URI_ATTRIBUTE);
    //shiro也有他的方法。详细看下面。

    如果需要保存登录之前的Request信息,那么需要在Login拦截的Filter中先保存:

    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response)
    		throws Exception {
    	//保存Request和Response,登录后可以取到
    	saveRequestAndRedirectToLogin(request, response);
    	return Boolean.FALSE ;
    }
    //登录后,取到之前的Request中的一些信息。
    SavedRequest saveRequest = WebUtils.getSavedRequest(request);
    saveRequest.getMethod();//之前的请求方法
    saveRequest.getQueryString();//之前请求的条件
    saveRequest.getRequestURI();//之前请求的路径
    saveRequest.getRequestUrl();//之前请求的全路径

    10、用户禁止登录Demo

    这个功能其实是一个改变用户数据库表里的一个字段,本Demo中:1:有效,0:禁止登录

    然后踢出用户登录状态。代码详细请查看CustomSessionManager.java类的forbidUserById(Long id, Long status)方法。

    而再次登录的话,需要再登录,而登录的地方限制了用户状态为(0:禁止登录)的用户登录。

    /**
     * 查询要禁用的用户是否在线。
     * @param id		用户ID
     * @param status	用户状态
     */
    public void forbidUserById(Long id, Long status) {
    	//获取所有在线用户
    	for(UserOnlineBo bo : getAllUser()){
    		Long userId = bo.getId();
    		//匹配用户ID
    		if(userId.equals(id)){
    			//获取用户Session
    			Session session = shiroSessionRepository.getSession(bo.getSessionId());
    			//标记用户Session
    			SessionStatus sessionStatus = (SessionStatus) session.getAttribute(SESSION_STATUS);
    			//是否踢出 true:有效,false:踢出。
    			sessionStatus.setOnlineStatus(status.intValue() == 1);
    			//更新Session
    			customShiroSessionDAO.update(session);
    		}
    	}
    }

    11、在线显示,在线用户管理(踢出登录)

    上面的功能依赖这个功能。从Redis中获取所有有效的Session。

    /**
     * 获取所有的有效Session用户
     * @return
     */
    public  List getAllUser() {
        /*获取所有session*/
        Collection sessions = customShiroSessionDAO.getActiveSessions();
        List list = new ArrayList();
    
        for (Session session : sessions) {
            UserOnlineBo bo = getSessionBo(session);
            if(null != bo){
                list.add(bo);
            }
        }
        return list;
    }

    踢出后,不能直接退出,要不然用户感觉莫名其妙。所有增加了一个Filter。SimpleAuthFilter.java如果标记为踢出,会提示用户。具体查看源码以及配合项目的使用。

    12、登录注册密码加密传输

    这个地方好多人纠结的。比如密码过于简单,即使加密后也能破解。如我们把密码:123456,加密后就是:e10adc3949ba59abbe56e057f20f883e

    这个很容易被识别,导致不安全,现在市面上的MD5破解其实就是把常用的数字,字母进行先加密,然后对比,美其名曰破解MD5。

    那怎么能让用户即使使用很简单的密码,也发现不了?

    本Demo的实现方式是:MD5(登录帐号 + “固定值” + 密码),把这个作为密码,这样,基本是不可能猜的出来。

    //Java代码。UserManager.md5Pswd(UUser user);
    /**
     * 加工密码,和登录一致。
     * @param user
     * @return
     */
    public static UUser md5Pswd(UUser user){
    	//密码为   email + '#' + pswd,然后MD5
    	user.setPswd(md5Pswd(user.getEmail(),user.getPswd()));
    	return user;
    }
    /**
     * 字符串返回值
     * @param email
     * @param pswd
     * @return
     */
    public static String md5Pswd(String email ,String pswd){
    	pswd = String.format("%s#%s", email,pswd);
    	pswd = MathUtil.getMD5(pswd);
    	return pswd;
    }
    //JS代码
    var pswd = MD5(username +"#" + password);

    13、密码修改

    不讲了,和上面原理一致,然后具体看Demo。

    14、用户个人中心

    包含的功能有[个人资料,资料修改,密码修改,我的权限],具体看Demo。

    15、权限的增删改查。

    Demo的设计是遵循RBAC3的思想。http://www.sojson.com/blog/142.html

    RBAC个人理解介绍:http://www.sojson.com/blog/141.html

    具体实现看Demo。

    16、角色的增删改查

    角色增删改查

    17、权限->角色->用户之间的关系维护

    把权限赋给角色。

    权限赋给角色

    把角色赋给用户。

    角色权限

    18、管理员权限的自动添加

    这里每次添加一个权限,都会添加到“管理员”角色下,保证“管理员”角色拥有最大权限。

    19、Spring定时任务数据初始化

    这个Demo因为是开放的,所以创建了一个定时任务。每20分钟执行一次,用Mysql存储过程重新创建表,重新插入初始化数据。

    具体数据看项目中的init/sql下的tables.sql(初始化表),init.data.sql(初始化数据)。

    //定时任务配置文件spring-timer.xml
    <task:executor id="executor" pool-size="5" />  
    <task:scheduler id="scheduler" pool-size="10" />  
    <task:annotation-driven executor="executor" scheduler="scheduler" />
    //Java 代码 RoleServiceImpl.java
    /**
     * 每20分钟执行一次
     */
    @Override
    @Scheduled(cron = "0 0/20 * * * ? ")
    public void initData() {
    	roleMapper.initData();
    }

    20、集成验证码

    shiro 验证码

    项目中package:com.sojson.common.utils.vcode包是验证码的封装包。

    并且提供了一个VerifyCodeUtils.java 的验证码工具类。

    使用方法参见:CommonController.java类中的getVCode()方法和getGifCode()方法。

    验证码详细介绍

    Java生成验证码合集(一)简单版 
    Java生成验证码合集(二)GJF版 

    四、项目代码截图

    QQ截图20171220140155.jpg

    注:本文著作权归作者,由demo大师发表,拒绝转载,转载需要作者授权

  • 相关阅读:
    C++ 之头文件依赖和引用类型的成员变量
    go语言学习之结构体
    go语言学习之解析XML
    VSCode编辑器使用技巧:快捷输入HTML代码(转)
    Qt QNetworkAccessManager请求导致的软件闪退
    注册表在64位操作系统下
    Signal和Slot是同步的还是异步的
    C++之private虚函数
    eclipse环境下Python报错"undefined variable from import..."的解决方案
    Android占位符
  • 原文地址:https://www.cnblogs.com/demodashi/p/8491157.html
Copyright © 2020-2023  润新知