Spring+Shiro权限管理 (二) 通过访问数据库实现用户的授权

前情提要,上一篇博客主要讲了Spring和Shiro整合(基于Shiro使用MD5加盐对密码加密,以及Shiro的认证流程)。

本篇基于上一篇博客,做以下两点补充:

一、基于Shiro对用户授权

二、Durid连接池监控

 

思路:

shiro查询是否有操作权限,前提要有个权限集合,这个集合从数据库中查询而来。

例如:

当点击新增用户,我们要在新增前,利用Shiro判断当前用户是否拥有新增用户的权限。

实际操作:

数据库中,通过当前用户信息查询当前用户的角色  》》》 在通过角色信息查询当前角色拥有的操作权限  》》》 拿到用户的操作权限集合

在UserRealm类中的doGetAuthorizationInfo授权方法中,simpleAuthorizationInfo.setStringPermissions(权限集合)。

 

具体实现步骤(比较乱,哈哈)

 

一、基于Shiro对用户授权

1、数据库设计(考虑表之间存在多对多关系)

① user(用户表)

② role(角色表)

③ permission(权限表)

④ user_role(用户和角色的中间表)

⑤ role_permission(角色和权限的关系表)  

2、因为修改了数据库结构,所以新增用户的方式也要修改下(新增用户时,也要设置角色)

①、在UserDao接口中添加添加角色的方法

public void addUserAndRole(@Param("idCode")Integer idCode,@Param("roleId") Integer roleId);

②、在UserDao.xml中添加:

    <insert id="addUserAndRole" parameterType="Integer" >
        INSERT INTO user_role (uid,rid) VALUES (#{idCode},#{roleId})
    </insert>

③、修改UserService中的addUser方法:

/*新增员工*/
public void addUser(User user,Integer roleId);

④、修改UserSreviceImp中addUser的实现(添加用户的同时向user_role表添加关系,这里用到了@Transactional事务注解)

    @Transactional
    @Override
    public void addUser(User user,Integer roleId) {

        userDao.addUser(user);
        /*同时绑定角色*/
        userDao.addUserAndRole(user.getIdCode(),roleId);
    }

⑤、spring引用事务注解的配置:

a、引入依赖:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-tx</artifactId>
    <version>${spring.version}</version>
</dependency>

b、applicationContext.xml头文件:

c、aplicationContext.xml中配置:

    <!-- 支持事务注解 -->
    <tx:annotation-driven transaction-manager="transactionManager"/>
    <!-- 配置事务管理组件 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>

3、引入AOP依赖(用于Shiro注解)

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>aopalliance</groupId>
            <artifactId>aopalliance</artifactId>
            <version>1.0</version>
        </dependency>

        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>1.9.4</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.4</version>
        </dependency>

4、applicationContext.xml

头部文件添加:

applicationContext.xml中添加:

    <!-- 支持事务注解 -->
    <tx:annotation-driven transaction-manager="transactionManager"/>
    <!-- 配置事务管理组件 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>

Shiro拦截器链中添加箭头指向的内容,

/admin = roles[admin] 意思是只有拥有admin角色的用户才可以访问/admin
/toAddUser = perms[add] 意思是只有拥有add权限的用户才可以访问/toAddUser

5、配置entity、dao、service、controller。

添加实体类(Role角色类、Permission权限类)

package com.xcj.jquery_ajax.entity;

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

@Setter
@Getter
@ToString
public class Role {
    Integer roleId;
    String roleName;
}
package com.xcj.jquery_ajax.entity;

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

@Setter
@Getter
@ToString
public class Permission {
    Integer permissionId;
    String permissionName;
}

新增RoleDao接口:

package com.xcj.jquery_ajax.dao;

import com.xcj.jquery_ajax.entity.Role;
import org.apache.ibatis.annotations.Param;

import java.util.List;
import java.util.Set;

public interface RoleDao {
    
    /*查询数据库中所有的角色*/
    public List<Role> findRole();

    /*通过用户编号获取用户角色名集合*/
    public Set<String> findRoleByIdCode(@Param("idCode") Integer idCode);

}

RoleDao.xml

<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.4//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.xcj.jquery_ajax.dao.RoleDao">
    <resultMap id="resultMap" type="Role">
        <id property="roleId" column="rid" javaType="Integer"/>
        <result property="roleName" column="rname" javaType="String"/>
    </resultMap>

    <select id="findRole" resultMap="resultMap">
        SELECT * FROM role
    </select>

    <select id="findRoleByIdCode" parameterType="Integer" resultType="String">
        SELECT r.rname FROM role r JOIN user_role ur ON ur.rid = r.rid WHERE ur.uid = #{idCode}
    </select>
</mapper>

PermissionDao接口

package com.xcj.jquery_ajax.dao;

import com.xcj.jquery_ajax.entity.Permission;
import org.apache.ibatis.annotations.Param;

import java.util.Set;

public interface PermissionDao {

    /*通过员工编号查询他的权限*/
    public Set<String> findPermissionByIdCode(@Param("idCode") Integer idCode);
}

PermissionDao.xml

<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.4//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.xcj.jquery_ajax.dao.PermissionDao">
    <resultMap id="resultMap" type="Permission">
        <id property="permissionId" column="pid" javaType="Integer"/>
        <result property="permissionName" column="pname" javaType="String"/>
    </resultMap>

    <select id="findPermissionByIdCode" parameterType="Integer" resultType="String">
        SELECT p.pname FROM permission p
        JOIN role_permission rp ON p.pid = rp.pid
        JOIN user_role ur ON ur.rid = rp.rid
        WHERE ur.uid = #{idCode}
    </select>

</mapper>

RoleService接口

package com.xcj.jquery_ajax.service;

import com.xcj.jquery_ajax.entity.Role;

import java.util.List;
import java.util.Set;

public interface RoleService {

    public List<Role> findRole();

    public Set<String> findRoleByIdCode(Integer idCode);
}

RoleServiceImp

package com.xcj.jquery_ajax.service.serviceImp;

import com.xcj.jquery_ajax.dao.RoleDao;
import com.xcj.jquery_ajax.entity.Role;
import com.xcj.jquery_ajax.service.RoleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Set;

@Service("RoleService")
public class RoleServiceImp implements RoleService {

    @Autowired
    RoleDao roleDao;

    @Override
    public List<Role> findRole() {
        return roleDao.findRole();
    }

    @Override
    public Set<String> findRoleByIdCode(Integer idCode) {
        return roleDao.findRoleByIdCode(idCode);
    }
}

PermissionService接口

package com.xcj.jquery_ajax.service;

import java.util.Set;

public interface PermissionService {

    public Set<String> findPermissionByIdCode(Integer idCode);
}

PermissionServiceImp

package com.xcj.jquery_ajax.service.serviceImp;

import com.xcj.jquery_ajax.dao.PermissionDao;
import com.xcj.jquery_ajax.entity.Permission;
import com.xcj.jquery_ajax.service.PermissionService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.Set;

@Service("PermissionService")
public class PermissionServiceImp implements PermissionService {

    @Autowired
    PermissionDao permissionDao;

    @Override
    public Set<String> findPermissionByIdCode(Integer idCode) {
        return permissionDao.findPermissionByIdCode(idCode);
    }
}

在UserController中新增几个方法用来测试权限

    @RequestMapping("admin")
    public ModelAndView admin(){
        return new ModelAndView("admin");
    }

    @RequiresRoles("admin2")
    @RequestMapping("admin2")
    public ModelAndView admin2(){
        return new ModelAndView("admin2");
    }

添加页面 admin.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>admin</title>
</head>
<body>
<h2>This is admin page!</h2>
</body>
</html>

admin2.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>admin2</title>
</head>
<body>
<h2>This is admin2 page!</h2>
</body>
</html>

修改home.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
    <head>
        <title>主页面</title>
    </head>
    <body>
        <h2>登录成功!</h2>
        <button onclick="window.location.href='toAddUser'">新增用户</button><br/>
        <button onclick="window.location.href='admin'">跳转admin页面</button><br/>
        <button onclick="window.location.href='admin2'">跳转admin2页面</button>
    </body>
</html>

6、自定义UserRealm中修改“授权”部分(重点)

package com.xcj.jquery_ajax.realm;

import com.xcj.jquery_ajax.entity.Permission;
import com.xcj.jquery_ajax.entity.Role;
import com.xcj.jquery_ajax.entity.User;
import com.xcj.jquery_ajax.service.PermissionService;
import com.xcj.jquery_ajax.service.RoleService;
import com.xcj.jquery_ajax.service.UserService;
import org.apache.commons.collections.CollectionUtils;
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.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.HashSet;
import java.util.Set;

public class UserRealm extends AuthorizingRealm {

    @Autowired
    UserService userService;

    @Autowired
    PermissionService permissionService;

    @Autowired
    RoleService roleService;

    /*授权*/
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {

        Integer idCode = Integer.valueOf((String)principalCollection.getPrimaryPrincipal());

        Set<String> roleSet = new HashSet<>();
        Set<String> permissionSet = new HashSet<>();

        if(idCode!=null){
            /*从数据库获取用户角色*/
            roleSet = roleService.findRoleByIdCode(idCode);

            /*从数据库获取用户权限*/
            permissionSet =  permissionService.findPermissionByIdCode(idCode);
        }

        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        /*有角色才有权限*/
        if (CollectionUtils.isNotEmpty(roleSet)){
            simpleAuthorizationInfo.setRoles(roleSet);
            if (CollectionUtils.isNotEmpty(permissionSet)){
                simpleAuthorizationInfo.setStringPermissions(permissionSet);
            }
            return simpleAuthorizationInfo;
        }else {
            return null;
        }
    }

    /*认证*/
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {

        UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;

        String username = token.getUsername();

        User user = null;
        if (username != null && !username.equals("")) {
            user = userService.findUserByIdCode(Integer.valueOf(username));
        }

        if (user != null) {
            String password = user.getPassword();
            String salt = user.getSalt();
            SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(username, password, ByteSource.Util.bytes(salt), this.getName());
            return simpleAuthenticationInfo;
        }
        return null;
    }
}

7、使用shiro注解方式判断角色、权限,不匹配时会报 AuthorizationException,我想出现该异常时跳到 unauthorized.jsp 页面,所以我对自定义异常类MyExceptionResolver 进行修改:

package com.xcj.jquery_ajax.execption;

import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.authz.AuthorizationException;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Slf4j
public class MyExceptionResolver implements HandlerExceptionResolver {
    @Override
    public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {

        if(e instanceof AuthorizationException){
           return new ModelAndView("unauthorized").addObject("exception",e.toString());
        }

        log.error("异常原因:{}  ;  异常信息:{}",e.getCause(),e.getMessage());

        ModelAndView mv = new ModelAndView("error");
        mv.addObject("exception", e.toString().replaceAll("\n", "<br/>"));
        return mv;
    }
}

unauthorized.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>unauthorized</title>
</head>
<body>
<h2>你没有权限访问!!!</h2><br/>
${exception}
</body>
</html>

以上就完成了授权的流程,下面进行测试效果展示:

数据数据中各表数据展示:

user表

role表

permission表

user_role表

role_permission表

先使用角色为customer的用户登录

点击跳转admin页面按钮(admin页面限制只让admin角色访问,而当前用户只有customer角色,故而跳到unauthorized页面!)

点击新增用户按钮(customer没有新增用户权限,故而跳到unauthorized页面!)

点击跳转admin2页面按钮(因为使用Shiro注解方式设置了当前方法需要拥有admin2角色的用户才能访问,而当前用户只有customer角色,故而跳到unauthorized页面,并且我们在自定义异常中对AuthorizationException异常进行了处理(在页面展示了主要的异常信息)!)

接下来,我们换成拥有所有权限的10001账号登录测试

演示结束,以上就是Shiro授权,权限管理的内容,如发现错误,欢迎指正,谢谢。

 

二、查看Durid连接池监控

在上一篇中已经配置应用了durid连接池(具体配置可查看上一篇),现在我想要查看监控的数据,所以我们访问http://localhost:8080/jquery_ajax/druid/,发现访问不了durid监控的登录界面,一直跳回登录界面。

原因:页面被shiro拦截了。

解决方法:在shiro配置文件中,放开对druid的拦截。

重启服务器结果就可以正常访问了

Shiro授权要点总结:

1、用户、角色、权限三表间的关系是多对多,所以需要在数据库建立了中间表。

2、在自定义Realm中的doGetAuthorizationInfo方法做授权处理,我们根据当前用户的idCode从数据库拿到对应的角色、权限,封装到SimpleAuthorizationInfo类中,最后返回该对象,判断是否拥有权限的工作交给shiro去执行。

3、使用shiro注解,当前用户调用了与当前角色或权限不符的方法时,shiro会返回AuthorizationException异常,我们应该捕获处理它,跳转页面或返回参数到前端的方式告诉操作者没有权限。

以上为本人对shiro授权的经验终结,如有错误,欢迎指正。

下一篇将讲述Shiro结合缓存的使用(有空会更新),谢谢。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值