基于shiro的按钮级别的权限管理系统

原文链接: https://my.oschina.net/tij/blog/3002760

一、项目背景

    作为程序猿的你,是否在大学课堂上听到老师讲权限管理一脸懵逼;是否在互联网上看到炫酷的权限管理系统一脸羡慕;是否在公司学习使用权限管理一脸激动。那么,今天你看到这个教程之后,请你不要再懵逼,请不要再羡慕,请肆无忌惮的激动吧。好嗨哟,即将带你走上人生的巅峰。下面将手把手的教你实现基于shiro权限框架的权限管理系统,真正意义上的在按钮级别上完成权限控制。注:本教程侧重点在于shiro框架的使用与权限控制逻辑的实现,对于其它知识点不在重点讨论范围内。如需学习更多关于shiro的知识可在我的文章查看。

二、技术栈

    后台:spring+springmvc+mybatis+shiro+kaptcha+fastjson+log4j+druid+maven+echcache+pageHelper等

    前端:vue+jquery+iview+ztree等

    数据库:mysql

    其它:IntelliJ IDEA 2018.2.4 x64+tomcat8.0+jdk1.8

三、项目结构

基于shiro的按钮级别的权限管理系统_第1张图片

基于shiro的按钮级别的权限管理系统_第2张图片

四、项目预览

基于shiro的按钮级别的权限管理系统_第3张图片

                                                        图1    登录首页

基于shiro的按钮级别的权限管理系统_第4张图片

                                                        图2    系统首页

基于shiro的按钮级别的权限管理系统_第5张图片

                                                            图3    用户列表

基于shiro的按钮级别的权限管理系统_第6张图片

                                                        图4    用户新增

基于shiro的按钮级别的权限管理系统_第7张图片

                                                            图5    角色列表

基于shiro的按钮级别的权限管理系统_第8张图片

                                                        图6    角色新增

基于shiro的按钮级别的权限管理系统_第9张图片

                                                        图7    菜单列表

基于shiro的按钮级别的权限管理系统_第10张图片

                                                        图8    菜单新增

五、数据库设计

    权限管理系统基础表有五张:sys_user(用户表)、sys_role(角色表)、sys_menu(资源表)、sys_user_role(用户角色关联表)、sys_role_menu(角色资源关联表),用户与角色和角色与资源都是多对多的关系,用户与资源必须通过授予角色才能建立关系。

基于shiro的按钮级别的权限管理系统_第11张图片

(1)用户表详细设计

基于shiro的按钮级别的权限管理系统_第12张图片

(2)角色表详细设计

基于shiro的按钮级别的权限管理系统_第13张图片

(3)资源表详细设计

基于shiro的按钮级别的权限管理系统_第14张图片

(4)用户角色表详细设计

基于shiro的按钮级别的权限管理系统_第15张图片

(5)角色资源表详细设计

基于shiro的按钮级别的权限管理系统_第16张图片

六、功能设计

(1)角色授权

        此处新建一个角色为“测试”,授予用户管理菜单权限、新增按钮权限、删除按钮权限。

基于shiro的按钮级别的权限管理系统_第17张图片

(2)分配角色

        此处新增新建一个用户“a”,分配“测试”角色。

基于shiro的按钮级别的权限管理系统_第18张图片

(3)新用户登录测试

    因为“a”用户分配的角色为“测试”,“测试”角色拥有用户管理菜单、新增按钮、删除按钮权限,用该用户登录系统后只能看到用户管理菜单、新增按钮、删除按钮;不会看到其它的系统菜单和修改按钮。        

基于shiro的按钮级别的权限管理系统_第19张图片

七、项目代码示例

        该项目的基础代码github地址为:https://github.com/tmAlj/shiro/tree/master/ssms,以下的给出的配置文件为本项目改变的内容,其余相同的部分未给出。

(1)pom.xml依赖管理配置



    4.0.0

    com.wsd
    ssms1
    1.0-SNAPSHOT
    
        
            
                org.apache.maven.plugins
                maven-compiler-plugin
                
                    7
                    7
                
            
        
    

    
        
        
            org.springframework
            spring-core
            4.3.3.RELEASE
        
        
            org.springframework
            spring-context
            4.3.3.RELEASE
        
        
            org.springframework
            spring-aspects
            4.3.3.RELEASE
        
        
            org.springframework
            spring-context-support
            4.3.3.RELEASE
        
        
            org.springframework
            spring-beans
            4.3.3.RELEASE
        
        
            org.springframework
            spring-expression
            4.3.3.RELEASE
        
        
            org.springframework
            spring-jdbc
            4.3.3.RELEASE
        
        
            org.springframework
            spring-tx
            4.3.3.RELEASE
        
        

        
        
            mysql
            mysql-connector-java
            5.1.40
        
        

        
        
            com.alibaba
            druid
            1.0.26
        
        

        
        
            org.mybatis
            mybatis
            3.4.1
        
        
            org.mybatis
            mybatis-spring
            1.3.0
        
        
            com.github.pagehelper
            pagehelper
            4.1.6
        
        

        
        
            org.springframework
            spring-webmvc
            4.3.3.RELEASE
        
        
            jstl
            jstl
            1.2
        
        
            taglibs
            standard
            1.1.2
        
        

        
        
            org.slf4j
            slf4j-api
            1.7.19
        
        
            org.slf4j
            slf4j-log4j12
            1.7.19
        
        
            log4j
            log4j
            1.2.17
        
        

        
        
            junit
            junit
            4.12
        
        

        
        
            com.alibaba
            fastjson
            1.2.20
        
        

        
        
            javax.servlet
            javax.servlet-api
            3.1.0
        
        

        
        
            commons-codec
            commons-codec
            1.10
        
        
            commons-configuration
            commons-configuration
            1.10
        
        
            commons-lang
            commons-lang
            2.6
        
        
            commons-fileupload
            commons-fileupload
            1.3.1
        
        
            commons-io
            commons-io
            2.5
        
        
            commons-logging
            commons-logging
            1.2
        
        

        
        
            org.apache.shiro
            shiro-core
            1.3.2
        
        
            org.apache.shiro
            shiro-ehcache
            1.3.2
        
        
            org.apache.shiro
            shiro-spring
            1.3.2
        
        
            org.apache.shiro
            shiro-web
            1.3.2
        
        

        
        
            com.github.axet
            kaptcha
            0.0.9
        
        
    
    

(2)web.xml配置




        
        tm-cli
        
            login.jsp
        

        
        
            contextConfigLocation
            
                classpath:spring-config.xml
            
        
        
            org.springframework.web.context.ContextLoaderListener
            
        

        
        
            encode
            org.springframework.web.filter.CharacterEncodingFilter
            
                encoding
                utf-8
            
        
        
            encode
            /*
        

        
        
            appServlet
            org.springframework.web.servlet.DispatcherServlet
            
                contextConfigLocation
                classpath:spring-mvc-config.xml
            
            1
            true
        
        
            appServlet
            /
        

        
        
            shiroFilter
            org.springframework.web.filter.DelegatingFilterProxy
            
                targetFilterLifecycle
                true
            
        
        
            shiroFilter
            /*
        

(3)shiro配置文件spring-shiro-config.xml配置




	
	

	
	
		
		
		
		
		
	

	
	
        
	

	
	

	
	
		
	
	
		
	

	
	
		
		
		
		
		
		
		
			
			
			
				/statics/** = anon
				/plugins/** = anon
				/login.jsp = anon
				/login = anon
				/logout = logout
				/captcha.jpg = anon
				/** = authc
			
		
	


(4)springmvc配置文件spring-mvc-config.xml配置



    
    
        
        
        
    

    
    
    
    

    
    
        
        
    

    
    

    
    
        
            
            
                
                    
                        application/json;charset=UTF-8
                        text/html;charset=UTF-8
                    
                
                
                    
                        WriteMapNullValue
                        QuoteFieldNames
                    
                
            
        
    

    
    
        
            
                
                    
                        no
                        black
                        5
                    
                
            
        
    

八、部分功能解析

(1)登录认证(认证参考)

        1.1    前端实现

<%@ page contentType="text/html;charset=UTF-8" language="java" %>



    
    
    
    用户登录
    
    


    
欢迎使用tm-cli管理系统
点击刷新
点击刷新
登    录

        1.2    controller实现

package com.wsd.controller;

import com.google.code.kaptcha.Constants;
import com.google.code.kaptcha.Producer;
import com.wsd.utils.ResultData;
import com.wsd.utils.ShiroUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.crypto.hash.Sha256Hash;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.imageio.ImageIO;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.IOException;

/**
 * Created by tm on 2018/8/26.
 * 登录controller
 */
@Controller()
public class LoginController {

    @Autowired
    private Producer producer; //验证码操作对象

    /**
     * 生成验证码
     * @param response
     * @throws ServletException
     * @throws IOException
     */
    @RequestMapping("captcha.jpg")
    public void captcha(HttpServletResponse response)throws ServletException, IOException {
        //页面不用缓存
        response.setHeader("Cache-Control", "no-store, no-cache");
        response.setContentType("image/jpeg");
        //生成文字验证码
        String text = producer.createText();
        //生成图片验证码
        BufferedImage image = producer.createImage(text);
        //验证码存入session,用于登录时做对比
        ShiroUtils.setSessionAttribute(Constants.KAPTCHA_SESSION_KEY, text);
        ServletOutputStream out = response.getOutputStream();
        //输出验证码
        ImageIO.write(image, "jpg", out);
    }

    /**
     * 登录
     */
    @ResponseBody
    @RequestMapping(value = "/login", method = RequestMethod.POST)
    public ResultData login(String account, String password, String code, String remember)throws IOException {
        String kaptcha = ShiroUtils.getKaptcha(Constants.KAPTCHA_SESSION_KEY); //session中获取保存的验证码内容
        if(!kaptcha.equalsIgnoreCase(code)){
            return ResultData.error("验证码不正确");
        }
        try{
            Subject subject = ShiroUtils.getSubject();
            password = new Sha256Hash(password).toHex();
            UsernamePasswordToken token = new UsernamePasswordToken(account, password);
            // 开启记住我的功能
            if(remember.equals("false")){
                token.setRememberMe(false);
            }else{
                token.setRememberMe(true);
            }
            subject.login(token);
        }catch (UnknownAccountException e) {
            return ResultData.error(e.getMessage());
        }catch (IncorrectCredentialsException e) {
            return ResultData.error(e.getMessage());
        }catch (LockedAccountException e) {
            return ResultData.error(e.getMessage());
        }catch (AuthenticationException e) {
            return ResultData.error("账户验证失败");
        }catch (Exception e) {
            return ResultData.error();
        }
        return ResultData.ok();
    }
}

        1.3    自定义loginRealm

package com.wsd.shiro;

import com.wsd.model.Menu;
import com.wsd.model.User;
import com.wsd.service.MenuService;
import com.wsd.service.UserService;
import com.wsd.service.impl.LoginServiceImpl;
import com.wsd.service.impl.MenuServiceImpl;
import com.wsd.service.impl.UserServiceImpl;
import org.apache.commons.lang.StringUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.*;

/**
 * Created by tm on 2018/8/26.
 * 自定义realm
 * 注:需要在spring-shiro-config.xml中配置
 */
public class LoginRealm extends AuthorizingRealm {

    @Autowired LoginServiceImpl lsi; //注入登录service
    @Autowired MenuServiceImpl msi; //注入菜单service
    @Autowired UserServiceImpl usi; //注入用户service

    /*授权*/
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        User user = (User)principalCollection.getPrimaryPrincipal();
        Long userId = user.getUserId();
        List permsList = null;
        //系统管理员,拥有最高权限
        if(userId == 1){
            List menuList = msi.queryList(new HashMap());
            permsList = new ArrayList(menuList.size());
            for(Menu menu : menuList){
                permsList.add(menu.getPerms());
            }
        }else{
            permsList = usi.queryAllPerms(userId);
        }

        //用户权限列表
        Set permsSet = new HashSet();
        for(String perms : permsList){
            if(StringUtils.isBlank(perms)){
                continue;
            }
            permsSet.addAll(Arrays.asList(perms.trim().split(",")));
        }
        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
        info.setStringPermissions(permsSet);
        return info;
    }

    /*验证*/
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        String account = (String) authenticationToken.getPrincipal(); //获取输入的账户
        String password = new String((char[]) authenticationToken.getCredentials()); //获取输入的密码

        //数据中查询用户信息
        User user = lsi.queryByUserName(account);

        //账号不存在
        if(user == null) {
            throw new UnknownAccountException("账号或密码不正确");
        }
        //密码错误
        if(!password.equals(user.getPassword())) {
            throw new IncorrectCredentialsException("账号或密码不正确");
        }
        //账号锁定
        if(user.getStatus() == 0){
            throw new LockedAccountException("账号已被锁定,请联系管理员");
        }
        SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, password, getName());
        return info;
    }
}

    (2)权限控制(授权参考)

            2.1    前端实现(通过shiro的标签控制前端权限标签参考)

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>



    
    
    
    用户管理
    
    


    
新增 修改 删除

        2.2    controller实现(通过shiro的权限注解控制请求注解参考)

package com.wsd.controller;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.wsd.base.BaseController;
import com.wsd.model.User;
import com.wsd.service.impl.LoginServiceImpl;
import com.wsd.service.impl.UserAndRoleServiceImpl;
import com.wsd.service.impl.UserServiceImpl;
import com.wsd.utils.ResultData;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

import java.util.List;

/**
 * Created by tm on 2018/8/26.
 * 系统用户controller
 */
@Controller
@RequestMapping("sys/user")
public class UserController extends BaseController {

    @Autowired UserServiceImpl usi; //注入系统用户service
    @Autowired LoginServiceImpl lsi; //注入登录service
    @Autowired UserAndRoleServiceImpl uarsi; //注入系统用户角色service

    /**
     * 访问user.jsp页面
     * @return
     */
    @RequestMapping()
    public String goUserPage(){
        return "user";
    }

    /**
     * 访问user_add.jsp页面
     * @return
     */
    @RequestMapping("/user_add")
    public String goUserAddPage(){
        return "user_add";
    }

    /**
     * 获取用户列表
     * @param page 当前页码
     * @param limit 每页显示条数
     * @return
     */
    @ResponseBody
    @RequestMapping("/list")
    @RequiresPermissions("sys:user:list")
    public ResultData getUserList(Integer page, Integer limit, String userName){
        //PageHelper分页插件
        PageHelper.startPage(page, limit);
        List userList = usi.queryList(userName);
        PageInfo p = new PageInfo(userList);
        return ResultData.ok().put("page", p);
    }

    /**
     * 用户信息
     */
    @ResponseBody
    @RequestMapping("/info")
    @RequiresPermissions("sys:user:info")
    public ResultData info(Long userId){
        //执行查询
        User userInfo = usi.queryObject(userId);
        //获取用户所属的角色列表
        List roleIdList = uarsi.queryRoleIdList(userId);
        userInfo.setRoleIdList(roleIdList);
        return ResultData.ok().put("userInfo", userInfo);
    }

    /**
     * 保存用户
     * @param user 用户实体
     * @return
     */
    @ResponseBody
    @RequestMapping("/save")
    @RequiresPermissions("sys:user:save")
    public ResultData saveUsers(@RequestBody User user){
        //判断用户名称是否可用
        User u = lsi.queryByUserName(user.getUsername());
        if(u != null){
            return ResultData.error("当前用户名称不能使用!");
        }
        usi.save(user);
        return ResultData.ok();
    }

    /**
     * 修改用户
     */
    @ResponseBody
    @RequestMapping("/update")
    @RequiresPermissions("sys:user:update")
    public ResultData update(@RequestBody User user){
        usi.update(user);
        return ResultData.ok();
    }

    /**
     * 删除用户
     * @param userIdList 用户id数组
     * @return
     */
    @ResponseBody
    @RequestMapping("/delete")
    @RequiresPermissions("sys:user:delete")
    public ResultData deleteUsers(@RequestBody Long[] userIdList){
        if(ArrayUtils.contains(userIdList, 1L)){
            return ResultData.error("系统管理员不能删除");
        }
        if(ArrayUtils.contains(userIdList, getUserId())){
            return ResultData.error("当前用户不能删除");
        }
        usi.deleteUser(userIdList);
        return ResultData.ok();
    }
}

九、参考文档

    iview参考文档

    vue参考文档

    ztree参考文档

    shiro参考文档

十、获取该项目源代码

    A:微信扫描下方二维码,打赏一杯咖啡钱

    B:打赏的时候留下您的邮箱地址,二十四小时内通过邮箱发送给您

基于shiro的按钮级别的权限管理系统_第20张图片

 

转载于:https://my.oschina.net/tij/blog/3002760

你可能感兴趣的:(基于shiro的按钮级别的权限管理系统)