文档章节

项目实录之Shiro的使用

FunGa
 FunGa
发布于 2015/07/29 11:56
字数 2323
阅读 303
收藏 11

阅读本文之前建议先阅读如下两篇文章



一、添加相关Jar包
    

在项目pom.xml添如下依赖:
     

<dependency>
            <groupId>taglibs</groupId>
            <artifactId>standard</artifactId>
            <version>1.1.2</version>
            <type>jar</type>
        </dependency>
        <!-- SECURITY begin -->
            <dependency>
                <groupId>org.apache.shiro</groupId>
                <artifactId>shiro-core</artifactId>
                <version>${shiro.version}</version>
            </dependency>
            <dependency>
                <groupId>org.apache.shiro</groupId>
                <artifactId>shiro-spring</artifactId>
                <version>${shiro.version}</version>
            </dependency>
            <dependency>
                <groupId>org.apache.shiro</groupId>
                <artifactId>shiro-web</artifactId>
                <version>${shiro.version}</version>
            </dependency>
            <dependency>
                <groupId>org.apache.shiro</groupId>
                <artifactId>shiro-ehcache</artifactId>
                <version>${shiro.version}</version>
            </dependency>
            <!-- SECURITY end -->


 


二、数据库表与数据

我们需要3个表,user表示用户,会初始化多个不同权限的用户;menu表示菜单,不同用户拥有不同的菜单;user_menu是用户与菜单的对照表,该用户拥有哪些菜单会在这里标记。实际开发者一般基于角色进行权限管理,为了方便起见,本示例直接基于用户了。

 

CREATE TABLE `user` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `username` varchar(20) NOT NULL,
  `password` varchar(20) NOT NULL,
  PRIMARY KEY (`id`,`username`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8
CREATE TABLE `menu` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `name` varchar(20) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
CREATE TABLE `user_menu` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `user_id` bigint(20) NOT NULL,
  `menu_id` bigint(20) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
INSERT INTO USER (id,username,PASSWORD) VALUES (1,'jobs','qwe123')
INSERT INTO USER (id,username,PASSWORD) VALUES (2,'gates','888888')
INSERT INTO menu (id,NAME) VALUES (1,'菜单一')
INSERT INTO menu (id,NAME) VALUES (2,'菜单二')
INSERT INTO menu (id,NAME) VALUES (3,'菜单三')
INSERT INTO menu (id,NAME) VALUES (4,'菜单四')
INSERT INTO menu (id,NAME) VALUES (5,'菜单五')
INSERT INTO user_menu (id,user_id,menu_id) VALUES (1,1,1)
INSERT INTO user_menu (id,user_id,menu_id) VALUES (2,1,2)
INSERT INTO user_menu (id,user_id,menu_id) VALUES (3,1,3)
INSERT INTO user_menu (id,user_id,menu_id) VALUES (4,2,3)
INSERT INTO user_menu (id,user_id,menu_id) VALUES (4,2,4)
INSERT INTO user_menu (id,user_id,menu_id) VALUES (4,2,5)

 

我们有jobs和gates两个用户,有菜单一、菜单二、菜单三、菜单四和菜单五五个菜单。jobs拥有菜单一、菜单二和菜单三,gates拥有菜单三、菜单四和菜单五。

三、编写相关代码


1、在dao层新建wang.wangci.bottae.dao.entity.Menu类
 
 

package wang.wangci.bottae.dao.entity;
public class Menu {
    private long id;
    private String name;
    public long getId() {
        return id;
    }
    public void setId(long id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    
    
}

 
2、修改dao层的User类

 

package wang.wangci.bottae.dao.entity;
import java.util.ArrayList;
import java.util.List;
public class User {
    private long id;
    private String username;
    private String password;
    private List<Menu> menuList= new ArrayList<Menu>();
    public long getId() {
        return id;
    }
    public void setId(long id) {
        this.id = id;
    }
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
    public List<Menu> getMenuList() {
        return menuList;
    }
    public void setMenuList(List<Menu> menuList) {
        this.menuList = menuList;
    }
    
}

 


3、在dao层新建 wang.wangci.bottae.dao.entity.UserMenu类

 

package wang.wangci.bottae.dao.entity;
public class UserMenu {
    private long id;
    private long userId;
    private long menuId;
    public long getId() {
        return id;
    }
    public void setId(long id) {
        this.id = id;
    }
    public long getUserId() {
        return userId;
    }
    public void setUserId(long userId) {
        this.userId = userId;
    }
    public long getMenuId() {
        return menuId;
    }
    public void setMenuId(long menuId) {
        this.menuId = menuId;
    }
    
}

 

4、在dao层新建wang.wangci.bottae.dao.mapper.MenuMapper

 

package wang.wangci.bottae.dao.mapper;
import java.util.List;
import org.apache.ibatis.annotations.Param;
import wang.wangci.bottae.dao.entity.Menu;
public interface MenuMapper {
    public List<Menu> selectsByUserId(@Param("userId")long userId);
}

 

4、修改UserMapper接口

 

package wang.wangci.bottae.dao.mapper;
import java.util.List;
public interface UserMapper {
    public List<User> selects();
    
    public User selectById(@Param("id")long id);
    
    public User selectByName(@Param("name")String name); 
}

 

5、修改UserMapper.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="wang.wangci.bottae.dao.mapper.UserMapper">
        
    <select id="selects" resultType="wang.wangci.bottae.dao.entity.User">
        SELECT *    FROM user
    </select>
    <select id="selectById" resultType="wang.wangci.bottae.dao.entity.User">
        SELECT *    FROM user WHERE id=#{id}
    </select>
    
    <select id="selectByName" resultType="wang.wangci.bottae.dao.entity.User">
        SELECT *    FROM user WHERE username=#{name}
    </select>
</mapper>


 


6、新增MenuMapper.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="wang.wangci.bottae.dao.mapper.MenuMapper">
        
    <select id="selectsByUserId" resultType="wang.wangci.bottae.dao.entity.Menu">
        SELECT 
        m.id,
        m.name
        FROM 
        menu m,
        user_menu u
        WHERE m.id = u.menu_id AND u.user_id=#{userId}
    </select>
</mapper>


 

7、service层修改UserService接口

 

package wang.wangci.bottae.service;
import java.util.List;
import wang.wangci.bottae.dao.entity.User;
public interface UserService {
    public List<User> finds();
    
    public User findById(long id);
    
    public User findByName(String name);
}
 
8、service层修改UserServiceImpl类
 
package wang.wangci.bottae.service.impl;
import java.util.List;
@Component
public class UserServiceImpl implements UserService {
    
    @Resource
    private UserMapper userMapper;
    @Override
    public List<User> finds() {
        return userMapper.selects();
    }
    @Override
    public User findById(long id) {
        return userMapper.selectById(id);
    }
    @Override
    public User findByName(String name) {
        return userMapper.selectByName(name);
    }
}

 

9、service层新增MenuService接口

 

package wang.wangci.bottae.service;
import java.util.List;
import wang.wangci.bottae.dao.entity.Menu;
public interface MenuService {
    public List<Menu> findsByUserId(long userId);
}

 

10、service新增MenuServiceImpl实现类

 

package wang.wangci.bottae.service.impl;
import java.util.List;
import javax.annotation.Resource;
import org.springframework.stereotype.Component;
import wang.wangci.bottae.dao.entity.Menu;
import wang.wangci.bottae.dao.mapper.MenuMapper;
import wang.wangci.bottae.service.MenuService;
@Component
public class MenuServiceImpl implements MenuService{
    
    @Resource
    private MenuMapper menuMapper;
    @Override
    public List<Menu> findsByUserId(long userId) {
        return menuMapper.selectsByUserId(userId);
    }
}

 

11、web层新建LoginController类
  注意:示例代码并不严谨,如需在实际开发中使用,请进行各种异常处理。

 

package wang.wangci.bottae.web.controllers;
import java.util.List;
@Controller
public class LoginController {
    
    @Resource
    private UserService userService;
    @Resource
    private MenuService menuService;
    
    @RequestMapping("/")
    public String index(String passwd,HttpServletRequest request, HttpServletResponse response,ModelMap model){
        String username = (String)request.getSession().getAttribute("username");
        User user = userService.findByName(username);
        if(user!=null){
            List<Menu> menus = menuService.findsByUserId(user.getId());
            model.addAttribute("menus", menus);
        }
        return "index";
    }
    
    @RequestMapping(value = "/login", method = {RequestMethod.GET,RequestMethod.POST})
    public String login(@RequestParam(value="username",defaultValue="",required=false)String username,
            @RequestParam(value="passwd",defaultValue="",required=false)String passwd,
            HttpServletRequest request, HttpServletResponse response, ModelMap model)throws Exception {
        
        UsernamePasswordToken token = new UsernamePasswordToken(username,passwd);
        Subject subject = SecurityUtils.getSubject();
        try {
            subject.login(token);
        }catch (UnknownAccountException uae) {
            model.addAttribute("message", "帐号不存在,请重新登录");
        }catch (IncorrectCredentialsException ice) {
            model.addAttribute("message", "密码错误,请重新登录");
        } catch (DisabledAccountException e) {
            model.addAttribute("message", "帐号状态异常,禁止登录,请联系系统管理员!");
        }
        
        if(subject.isAuthenticated()){
            HttpSession session = request.getSession();
            session.setAttribute("username", username);
            return "redirect:/";
        }else{
            token.clear();
        }
        return "login";
        
    }
    
    @RequestMapping("/logout")
    public String logout(HttpServletRequest request,HttpServletResponse response,ModelMap model){
        SecurityUtils.getSubject().logout();
        return "redirect:/login";
    }
}

 

 12、web层新建HasAnyPermissionsTag类

 

package wang.wangci.bottae.web.security;
import org.apache.shiro.subject.Subject;
public class HasAnyPermissionsTag extends PermissionTag{
    private static final long serialVersionUID = 1L;
    private static final String PERMISSION_NAMES_DELIMETER = ",";
    @Override
    protected boolean showTagBody(String permissionNames) {
        boolean hasAnyPermission = false;
        Subject subject = getSubject();
        if (subject != null) {
            for (String permission : permissionNames
                    .split(PERMISSION_NAMES_DELIMETER)) {
                if (subject.isPermitted(permission.trim())) {
                    hasAnyPermission = true;
                    break;
                }
            }
        }
        return hasAnyPermission;
    }
}

 

13、web层新建Principal类

 

package wang.wangci.bottae.web.security;
import java.io.Serializable;
public class Principal implements Serializable{
    private static final long serialVersionUID = 1L;
    
    private long id; // 编号
    private String name; // 姓名
    
    public Principal(User user) {
        this.id = user.getId();
        this.name = user.getUsername();
    }
    public long getId() {
        return id;
    }
    public void setId(long id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    
    
    
}

 

14、web层新建SystemAuthorizingRealm类

 

package wang.wangci.bottae.web.security;
import java.util.List;
public class SystemAuthorizingRealm extends AuthorizingRealm {
    private static final Logger logger = LoggerFactory
            .getLogger(SystemAuthorizingRealm.class);
    @Resource
    private UserService userService;
    @Resource
    private MenuService menuService;
    /**
     * 认证回调函数, 登录时调用
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(
            AuthenticationToken authcToken) {
        UsernamePasswordToken token = (UsernamePasswordToken) authcToken;
        // 校验用户名密码
        User user = null;
        try {
            user = userService.findByName(token.getUsername());
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (user != null) {
            
            return new SimpleAuthenticationInfo(new Principal(user),
                    user.getPassword(), getName());
        } else {
            return null;
        }
    }
    /**
     * 授权查询回调函数, 进行鉴权但缓存中无用户的授权信息时调用
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(
            PrincipalCollection principals) {
        Principal principal = (Principal) getAvailablePrincipal(principals);
        User user = null;
        try {
            user = userService.findByName(principal.getName());
        } catch (Exception e) {
            logger.info("查询登录用户出错了",e);
        }
        if (user != null) {
            SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
            //是否系统默认高级用户
            List<Menu> list = menuService.findsByUserId(user.getId());
            
            for (Menu menu : list) {
                    // 添加基于Permission的权限信息
                    info.addStringPermission(menu.getName());
            }
            // 添加用户权限
            info.addStringPermission("user");
            // 添加用户角色信息
            return info;
        } else {
            return null;
        }
    }
}

 

15、在resources/context下新建context-shiro.xml文件

 

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.2.xsd"
    default-lazy-init="true">
    <description>Shiro安全配置</description>
    
    <!-- 項目自定义的Realm -->
    <bean id="shiroDbRealm" class="wang.wangci.bottae.web.security.SystemAuthorizingRealm" >
    </bean>
    
    <!-- Shiro Filter -->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <property name="securityManager" ref="securityManager" />
        <property name="loginUrl" value="/login" />
        <property name="successUrl" value="/" />
        <property name="unauthorizedUrl" value="/logout" />
        <property name="filterChainDefinitions">
            <value>
                /login = anon
                /logout = logout
                /static/** = anon
                /** = authc
            </value>
        </property>
    </bean>
    <!-- 用户授权信息Cache, 采用EhCache -->
    <bean id="shiroEhcacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager">
        <!-- <property name="cacheManagerConfigFile" value="classpath:security/ehcache-shiro.xml"/> -->
        <!-- <property name="cacheManager" ref="cacheManagerFactory" /> -->
    </bean>
    
    <!-- Shiro's main business-tier object for web-enabled applications -->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <property name="realm" ref="shiroDbRealm" />
        <property name="cacheManager" ref="shiroEhcacheManager" />
    </bean>
    
    
    <!-- 保证实现了Shiro内部lifecycle函数的bean执行 -->
    <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>
    
    <!-- AOP式方法级权限检查  -->
    <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" depends-on="lifecycleBeanPostProcessor">
        <property name="proxyTargetClass" value="true" />
    </bean>
    <bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
        <property name="securityManager" ref="securityManager"/>
    </bean>
</beans>


  16、在WEB-INF下面新建目录tlds,在tlds目录下新建文件shiros-ext.tld

 

<?xml version="1.0" encoding="UTF-8" ?>
<!-- shiro 自定义标签 -->
<!DOCTYPE taglib PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.2//EN"
  "http://java.sun.com/dtd/web-jsptaglibrary_1_2.dtd">
<taglib>
    <tlib-version>1.1.2</tlib-version>
    <jsp-version>1.2</jsp-version>
    <short-name>Apache Shiro</short-name>
    <uri>http://shiro.apache.org/tagss</uri>
    <description>Apache Shiro JSP Tag Library extends the hasAnyPermissions
        tag.</description>
    <tag>
        <name>hasAnyPermissions</name>
        <tag-class>wang.wangci.bottae.web.security.HasAnyPermissionsTag</tag-class>
        <body-content>JSP</body-content>
        <description>Displays body content only if the current user has one of
            the specified permissions from a
            comma-separated list of permission
            names.
        </description>
        <attribute>
            <name>name</name>
            <required>true</required>
            <rtexprvalue>true</rtexprvalue>
        </attribute>
    </tag>
</taglib>


 17、在views目录西新建include文件夹,在include下新建taglib.jsp

 

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
<%@ taglib prefix="fn" uri="http://java.sun.com/jsp/jstl/functions"%>
<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags"%>
<%@ taglib prefix="shiro-ext" uri="/WEB-INF/tlds/shiros-ext.tld"%>
<c:set var="ctx" value="${pageContext.request.contextPath}" />
<c:set var="ctxStatic" value="${pageContext.request.contextPath}/static" />


 18、在views下新建login.jsp文件

 

<%@ page language="java" pageEncoding="UTF-8"%>
<!DOCTYPE HTML>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>UUID</title>
</head>
<body>
    <form action="/login">
        用戶名:<input type="text" name='username'>
        密碼:<input type='password' name='passwd'>
        
        <input type="submit" value="登錄">
    </form>
</body>
</html>


 19、在views目录下新建index.jsp文件

 

<%@ page language="java" pageEncoding="UTF-8"%>
<%@ include file="/WEB-INF/views/include/taglib.jsp"%>
<!DOCTYPE HTML>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>UUID</title>
</head>
<body>
你好,${username},你有下面這些權限!<br>
        <shiro-ext:hasAnyPermissions name="菜单一">
            菜单一<br>
        </shiro-ext:hasAnyPermissions>
        <shiro-ext:hasAnyPermissions name="菜单二">
            菜单二<br>
        </shiro-ext:hasAnyPermissions>
        <shiro-ext:hasAnyPermissions name="菜单三">
            菜单三<br>
        </shiro-ext:hasAnyPermissions>
        <shiro-ext:hasAnyPermissions name="菜单四">
            菜单四<br>
        </shiro-ext:hasAnyPermissions>
        <shiro-ext:hasAnyPermissions name="菜单五">
            菜单五<br>
        </shiro-ext:hasAnyPermissions>
        
        <a href="/logout">退出登录</a>
</body>
</html>

 

20、在web.xml中增加一段拦截配置

       

  <!-- Shiro Security filter -->  
      
    <filter>  
        <filter-name>shiroFilter</filter-name>  
        <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>  
    </filter>  
    <filter-mapping>  
        <filter-name>shiroFilter</filter-name>  
        <url-pattern>/*</url-pattern>  
        <dispatcher>REQUEST</dispatcher>  
        <dispatcher>FORWARD</dispatcher>  
    </filter-mapping>

 

 四、运行测试


编译运行起服务。
浏览器输入:localhost:7777,都会被拦截到登录页面。在登录页输入jobs密码qwe123,登录,如果显示jobs拥有菜单一、菜单二、菜单三,说明成功了,点击退出登录,
使用gates/888888登录,如果显示拥有菜单三、菜单四、菜单五说明成功了。 


本文源码:

360云盘下载  访问密码 0385

进入我的博客 查看原文

© 著作权归作者所有

共有 人打赏支持
FunGa
粉丝 5
博文 10
码字总数 11477
作品 0
东城
私信 提问
oauth2-shiro 添加 redis 实现版本

oauth2-shiro-redis Integrate oauth2-shiro with Redis 说明 该项目具有 oauth2-shiro 的所有功能, 并添加了对 Redis 的支持 从 oauth2-shiro fork 的版本: 0.1-rc 项目使用的 Redis 版本信...

monkeyk7
2016/07/08
1K
0
oauth2-shiro-redis

oauth2-shiro-redis Integrate oauth2-shiro with Redis 说明 该项目具有 oauth2-shiro 的所有功能, 并添加了对 Redis 的支持 从 oauth2-shiro fork 的版本: 0.1-rc 项目使用的 Redis 版本信...

monkeyk7
2016/07/09
1K
1
云梦/shiro-struts2

#shiro-struts2 简单整合,更强及自身业务需要自己实现扩展 shiro本身是插件式的,所以ORM层框架可更换,web层框架可更换, 使用shiro最简单的是使用shiro本身提供的已有的类,自己只需扩展一...

云梦
2014/10/29
0
0
领课网络(RonCoo)/shiro-freemarker-tags

Apache Shiro tags for Freemarker 本项目是基于 shiro-freemarker-tags,由于在maven中央库里面没有找到,所以fork了过来,同时对项目进行了jar版本的升级,便于后面的开发。 更新 2017-03-...

领课网络(RonCoo)
04/06
0
0
SpringBoot集成Shiro并用MongoDB做Session存储

之前项目鉴权一直使用的Shiro,那是在Spring MVC里面使用的比较多,而且都是用XML来配置,用Shiro来做权限控制相对比较简单而且成熟,而且我一直都把Shiro的session放在mongodb中,这个比较符...

小红牛
2018/01/04
0
0

没有更多内容

加载失败,请刷新页面

加载更多

Java并发编程

并发与并行 并发指的是同时应对多个事件的能力,并行指的是同时做多件事的能力。 位级并行:32位计算机能够同时处理32位数运算,而8位计算机却要进行多次运算。 指令级并行:表面上看cpu是串...

春哥大魔王的博客
30分钟前
2
0
js数组遍历和对象遍历

数组遍历 for for(var i=0,len=arr.length;i<len;i++){console.log(arr[i]);} forEach - ES5语法,性能比for弱,不能使用break终止循环,不能使用return arr.forEach(function(item,inde......

祖达
49分钟前
3
0
Spring "reg:zookeeper" 的前缀 "reg" 未绑定等类似问题解决方案。

今天同事遇到一个Spring启动加载配置文件时,不识别reg:zookeeper标签的问题。 我查看配置,发现是Spring配置文件的头部没有引入reg标签的命名空间,具体如下图: 所以,以后遇到类似的标签未...

花漾年华
今天
2
0
阿里云领衔云市场

近期,2018年Q4及全年的全球云基础设施服务市场数据新鲜出炉,发布方是美国市场研究机构Synergy Research Group。这个机构是专做电信网络市场情报的公司,成立于1999年,每年都会公布各大公有...

linuxCool
今天
2
0
C++友元函数和友元类(C++ friend)详解

私有成员只能在类的成员函数内部访问,如果想在别处访问对象的私有成员,只能通过类提供的接口(成员函数)间接地进行。这固然能够带来数据隐藏的好处,利于将来程序的扩充,但也会增加程序书...

shzwork
今天
3
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部