项目实录之Shiro的使用

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



一、添加相关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

进入我的博客 查看原文

转载于:https://my.oschina.net/funga/blog/485093

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值