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

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

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

一、基于Shiro对用户授权

二、Durid连接池监控

 

一、基于Shiro对用户授权

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

① user(用户表)

Spring+Shiro权限管理 (二) 通过访问数据库实现用户的授权_第1张图片

② role(角色表)

Spring+Shiro权限管理 (二) 通过访问数据库实现用户的授权_第2张图片

③ permission(权限表)

Spring+Shiro权限管理 (二) 通过访问数据库实现用户的授权_第3张图片

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

Spring+Shiro权限管理 (二) 通过访问数据库实现用户的授权_第4张图片

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

Spring+Shiro权限管理 (二) 通过访问数据库实现用户的授权_第5张图片

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

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

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

②、在UserDao.xml中添加:

    
        INSERT INTO user_role (uid,rid) VALUES (#{idCode},#{roleId})
    

③、修改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、引入依赖:


    org.springframework
    spring-tx
    ${spring.version}

b、applicationContext.xml头文件:

Spring+Shiro权限管理 (二) 通过访问数据库实现用户的授权_第6张图片

c、aplicationContext.xml中配置:

    
    
    
    
        
    

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

        
            org.springframework
            spring-aop
            ${spring.version}
        
        
            aopalliance
            aopalliance
            1.0
        

        
            org.aspectj
            aspectjrt
            1.9.4
        
        
            org.aspectj
            aspectjweaver
            1.9.4
        

4、applicationContext.xml

头部文件添加:

Spring+Shiro权限管理 (二) 通过访问数据库实现用户的授权_第7张图片

applicationContext.xml中添加:

    
    
    
    
        
    

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

Spring+Shiro权限管理 (二) 通过访问数据库实现用户的授权_第8张图片

/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 findRole();

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

}

RoleDao.xml





    
        
        
    

    

    

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 findPermissionByIdCode(@Param("idCode") Integer idCode);
}

PermissionDao.xml





    
        
        
    

    

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 findRole();

    public Set 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 findRole() {
        return roleDao.findRole();
    }

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

PermissionService接口

package com.xcj.jquery_ajax.service;

import java.util.Set;

public interface PermissionService {

    public Set 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 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" %>


    admin


This is admin page!

admin2.jsp

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


    admin2


This is admin2 page!

修改home.jsp

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

    
        主页面
    
    
        

登录成功!



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 roleSet = new HashSet<>();
        Set 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", "
")); return mv; } }

unauthorized.jsp

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


    unauthorized


你没有权限访问!!!


${exception}

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

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

user表

role表

permission表

Spring+Shiro权限管理 (二) 通过访问数据库实现用户的授权_第9张图片

user_role表

role_permission表

Spring+Shiro权限管理 (二) 通过访问数据库实现用户的授权_第10张图片

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

Spring+Shiro权限管理 (二) 通过访问数据库实现用户的授权_第11张图片

Spring+Shiro权限管理 (二) 通过访问数据库实现用户的授权_第12张图片

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

Spring+Shiro权限管理 (二) 通过访问数据库实现用户的授权_第13张图片

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

Spring+Shiro权限管理 (二) 通过访问数据库实现用户的授权_第14张图片

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

Spring+Shiro权限管理 (二) 通过访问数据库实现用户的授权_第15张图片

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

Spring+Shiro权限管理 (二) 通过访问数据库实现用户的授权_第16张图片

Spring+Shiro权限管理 (二) 通过访问数据库实现用户的授权_第17张图片

Spring+Shiro权限管理 (二) 通过访问数据库实现用户的授权_第18张图片

Spring+Shiro权限管理 (二) 通过访问数据库实现用户的授权_第19张图片

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

 

二、查看Durid连接池监控

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

原因:页面被shiro拦截了。

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

Spring+Shiro权限管理 (二) 通过访问数据库实现用户的授权_第20张图片

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

Spring+Shiro权限管理 (二) 通过访问数据库实现用户的授权_第21张图片

Spring+Shiro权限管理 (二) 通过访问数据库实现用户的授权_第22张图片

Shiro授权要点总结:

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

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

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

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

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

你可能感兴趣的:(shiro授权,ssm,druid,Shiro)