【SpringBoot】Shiro(七)SpringBoot下授权

在这里插入图片描述

引言:Shiro是apache旗下一个开源框架,它将软件系统的安全认证相关的功能抽取出来,实现用户身份认证,权限授权、加密、会话管理等功能,组成了一个通用的安全认证框架

Shiro授权实现方式

代码设置的伪数据代码均为xiaozhang用户名,密码123456,角色为user。
shiro有三种授权实现方式,如下:

  • 编程式

    Subject subject = SecurityUtils.getSubject();
    if(subject.hasRole(“admin”)) {
    	//有权限
    } else {
    	//无权限
    }
    
  • 注解式

    @RequiresRoles("admin")
    public void hello() {
    	//有权限
    }
    
  • 标签式

    JSP/GSP 标签:在JSP/GSP 页面通过相应的标签完成:
    <shiro:hasRole name="admin">
    	<!— 有权限—>
    </shiro:hasRole>
    注意: Thymeleaf 中使用shiro需要额外集成!
    

标签式授权

0.页面资源授权 index.jsp

  • shiro模板—— <%@taglib prefix=“shiro” uri=“http://shiro.apache.org/tags” %>
  • 多角色管理—— <%–具有admin和user才能看见–%>
    <shiro:hasAnyRoles name=“admin,user”>
  • 单角色管理—— <%–具有admin用户才能看见–%>
    <shiro:hasRole name=“admin”>
<%@page contentType="text/html; UTF-8" pageEncoding="UTF-8" isELIgnored="false" %>
<%@taglib prefix="shiro" uri="http://shiro.apache.org/tags"%>
<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
<%--受限资源--%>
    <h1>系统主页v1.0</h1>
    <a href="${pageContext.request.contextPath}/user/logout">退出登录</a>
    <ul>
        <%--具有admin和user才能看见--%>
        <shiro:hasAnyRoles name="admin,user">
            <li><a href="">用户管理</a></li>
        </shiro:hasAnyRoles>
        <%--具有admin用户才能看见--%>
        <shiro:hasRole name="admin">
            <li><a href="">商品管理</a></li>
            <li><a href="">订单管理</a></li>
            <li><a href="">物流管理</a></li>
        </shiro:hasRole>
    </ul>
</body>
</html>

1.自定义Realm的授权处理 CustomerRealm

  • 获取身份信息 String primaryPrincipal = (String) principal.getPrimaryPrincipal();
  • 伪数据判断、根据主身份信息获取角色和权限信息 if (“xiaozhang”.equals(primaryPrincipal)){……
  • 伪数据:如果用户登录的用户名为xiaozhang则是user普通用户
  • 在index.jsp看到对应的标签权限
public class CustomerRealm extends AuthorizingRealm {
    //授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principal) {
        //获取身份信息
        String primaryPrincipal = (String) principal.getPrimaryPrincipal();
        System.out.println("调用授权验证"+primaryPrincipal);
        //伪数据、根据主身份信息获取角色和权限信息
        if ("xiaozhang".equals(primaryPrincipal)){
            SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
            //权限为普通用户
            simpleAuthorizationInfo.addRole("user");
            return simpleAuthorizationInfo;
        }

        return null;
    }


    //认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        //伪代码数据
        //获取到前台传输的token
        String principal = (String) token.getPrincipal();
        //在工厂工具类ApplicationContextUtils中获取service对象,默认策略是首字母小写,获取我们的userServiceImpl对象
        UserService userService = (UserService) ApplicationContextUtils.getBean("userServiceImpl");
        //调用UserService
        User user = userService.findByUserName(principal);
//      旧版伪代码
//        if ("xiaozhang".equals(principal)){
//            //成功返回创建的对象信息
//            return new SimpleAuthenticationInfo(principal,"123",this.getName());
//        }
        //判断
        //第一个参数用户名
        //第二个参数密码
        //第三个参数随机盐
        //第四个参数是Realm的名字
        if (!ObjectUtils.isEmpty(user)){
            return new SimpleAuthenticationInfo(user.getUsername(),user.getPassword(), ByteSource.Util.bytes(user.getSalt()),this.getName());
        }

        return null;
    }
}

  • 用户为xiaozhang的普通用户user只能看到用户管理权限
- 参考英文标识
admin管理员
user普通用户
guest游客

在这里插入图片描述


权限字符串式授权(标签处)

  • 权限字符串式授权—— <shiro:hasPermission name=“user:add:*”>

0.页面资源授权index.jsp

<%@page contentType="text/html; UTF-8" pageEncoding="UTF-8" isELIgnored="false" %>
<%@taglib prefix="shiro" uri="http://shiro.apache.org/tags"%>
<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
<%--受限资源--%>
    <h1>系统主页v1.0</h1>
    <a href="${pageContext.request.contextPath}/user/logout">退出登录</a>
    <ul>
        <%--具有admin和user才能看见--%>
        <shiro:hasAnyRoles name="admin,user">
            <li><a href="">用户管理</a>
                <ul>
                    <shiro:hasPermission name="user:add:*">
                        <li><a href="">添加</a></li>
                    </shiro:hasPermission>
                    <shiro:hasPermission name="user:delete:*">
                        <li><a href="">删除</a></li>
                    </shiro:hasPermission>
                    <shiro:hasPermission name="user:update:*">
                        <li><a href="">修改</a></li>
                    </shiro:hasPermission>
                    <shiro:hasPermission name="user:find:*">
                        <li><a href="">查询</a></li>
                    </shiro:hasPermission>
                </ul>
            </li>
        </shiro:hasAnyRoles>
        <%--具有admin用户才能看见--%>
        <shiro:hasRole name="admin">
            <li><a href="">商品管理</a></li>
            <li><a href="">订单管理</a></li>
            <li><a href="">物流管理</a></li>
        </shiro:hasRole>
    </ul>
</body>
</html>

1.自定义Realm的授权处理 CustomerRealm

  • 权限字符串式授权 simpleAuthorizationInfo.addStringPermission(“user:find:*”);
  • 在index.jsp看到对应的标签权限
public class CustomerRealm extends AuthorizingRealm {
    //授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principal) {
        //获取身份信息
        String primaryPrincipal = (String) principal.getPrimaryPrincipal();
        System.out.println("调用授权验证"+primaryPrincipal);
        //伪数据、根据主身份信息获取角色和权限信息
        if ("xiaozhang".equals(primaryPrincipal)){
            SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
            //权限为普通用户
            simpleAuthorizationInfo.addRole("user");
            //普通用户只能进行查询和修改权限
            simpleAuthorizationInfo.addStringPermission("user:find:*");
            simpleAuthorizationInfo.addStringPermission("user:update:*");
            return simpleAuthorizationInfo;
        }

        return null;
    }


    //认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        //伪代码数据
        //获取到前台传输的token
        String principal = (String) token.getPrincipal();
        //在工厂工具类ApplicationContextUtils中获取service对象,默认策略是首字母小写,获取我们的userServiceImpl对象
        UserService userService = (UserService) ApplicationContextUtils.getBean("userServiceImpl");
        //调用UserService
        User user = userService.findByUserName(principal);
//      旧版伪代码
//        if ("xiaozhang".equals(principal)){
//            //成功返回创建的对象信息
//            return new SimpleAuthenticationInfo(principal,"123",this.getName());
//        }
        //判断
        //第一个参数用户名
        //第二个参数密码
        //第三个参数随机盐
        //第四个参数是Realm的名字
        if (!ObjectUtils.isEmpty(user)){
            return new SimpleAuthenticationInfo(user.getUsername(),user.getPassword(), ByteSource.Util.bytes(user.getSalt()),this.getName());
        }

        return null;
    }
}

在这里插入图片描述

  • 用户为xiaozhang的普通用户user只能看到用户管理权限,然后只能操作查询和修改操作

编程式授权(方法体内)

0.新建OrderController

  • 编程式授权——subject.hasRole(“admin”)
  • 判断是否登录用户为admin
  • `是admin的话进入http://localhost:8089/shiro/order/save提示保存订单,否则为无权访问``
@Controller
@RequestMapping("order")
public class OrderController {

    //save方法
    @RequestMapping("save")
    public String save(){
        Subject subject = SecurityUtils.getSubject();
        if (subject.hasRole("admin")){
            System.out.println("保存订单");
        }else {
            System.out.println("无权访问");
        }
        return "redirect:/index.jsp";
    }
}

在这里插入图片描述


注解式编程(方法处)

0.使用注解OrderController

  • @RequiresRoles("admin")//用来判断角色,只有当admin时候才能进入
  • @RequiresRoles(value = {"admin","user"}) //同时具有admin和user角色才能进入
  • @RequiresPermissions("user:update:01")//用来判断权限字符串
@Controller
@RequestMapping("order")
public class OrderController {

    //save方法
    @RequestMapping("save")
    @RequiresRoles("admin")//用来判断角色,只有当admin时候才能进入
//    @RequiresRoles(value = {"admin","user"}) //同时具有admin和user角色才能进入
    public String save(){
        Subject subject = SecurityUtils.getSubject();
        System.out.println("保存");
        if (subject.hasRole("admin")){
            System.out.println("保存订单");
        }else {
            System.out.println("无权访问");
        }
        return "redirect:/index.jsp";
    }
}

在这里插入图片描述

  • 登录账号xiaozhang为user角色,访问http://localhost:8089/shiro/order/save,注解判断没有权限进入

授权在数据持久化

Shiro授权实现方式部分(上文)实现的都是通过伪代码的方式实现,实际生产过程中,我们是通过数据持久化在数据库中实现的,接下来的教程就教如何实现持久化

设计角色和权限表

在这里插入图片描述

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for t_pers
-- ----------------------------
DROP TABLE IF EXISTS `t_pers`;
CREATE TABLE `t_pers` (
  `id` int(6) NOT NULL AUTO_INCREMENT,
  `name` varchar(80) DEFAULT NULL,
  `url` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- ----------------------------
-- Table structure for t_role
-- ----------------------------
DROP TABLE IF EXISTS `t_role`;
CREATE TABLE `t_role` (
  `id` int(6) NOT NULL AUTO_INCREMENT,
  `name` varchar(60) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- ----------------------------
-- Table structure for t_role_perms
-- ----------------------------
DROP TABLE IF EXISTS `t_role_perms`;
CREATE TABLE `t_role_perms` (
  `id` int(6) NOT NULL,
  `roleid` int(6) DEFAULT NULL,
  `permsid` int(6) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

-- ----------------------------
-- Table structure for t_user
-- ----------------------------
DROP TABLE IF EXISTS `t_user`;
CREATE TABLE `t_user` (
  `id` int(6) NOT NULL AUTO_INCREMENT,
  `username` varchar(40) DEFAULT NULL,
  `password` varchar(40) DEFAULT NULL,
  `salt` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Table structure for t_user_role
-- ----------------------------
DROP TABLE IF EXISTS `t_user_role`;
CREATE TABLE `t_user_role` (
  `id` int(6) NOT NULL,
  `userid` int(6) DEFAULT NULL,
  `roleid` int(6) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

SET FOREIGN_KEY_CHECKS = 1;

授权实现

设计思路

- 建立角色表和权限表
- 登录用户小张角色为admin,拥有index.jsp里面所有权限,登录用户xiaodeng角色,只拥有index.jsp的用户管理的查询功能
- 登录时候创建一个返回信息对象,存放权限字符串,登录用户根据用户名查询角色,根据角色id查询权限
- 在前台渲染

0…创建dao方法UserDAO

  • 根据用户名查询所有角色
    User findRolesByUserName(String username);

  • 根据角色id查询权限集合
    List findPermsByRoleId(String id);

@Mapper
public interface UserDAO {
    //注册保存用户
    void save(User user);

    //登录验证数据库用户
    User findByUserName(String username);

    //根据用户名查询所有角色
    User findRolesByUserName(String username);

    //根据用户id查询权限集合
    List<Perms> findPermsByRoleId(String id);
}

1.mapper实现UserDAOMapper

<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ryoujou.dao.UserDAO">

    <insert id="save" parameterType="User" useGeneratedKeys="true" keyProperty="id">
        insert into t_user values(#{id},#{username},#{password},#{salt})
    </insert>

    <select id="findByUserName" parameterType="String" resultType="User">
      select id,username,password,salt from t_user
      where username = #{username}
    </select>

    <resultMap id="userMap" type="User">
        <id column="uid" property="id"/>
        <result column="username" property="username"/>
        <!--角色信息-->
        <collection property="roles" javaType="list" ofType="Role">
            <id column="id" property="id"/>
            <result column="rname" property="name"/>
        </collection>
    </resultMap>

    <select id="findRolesByUserName" parameterType="String" resultMap="userMap">
      SELECT u.id uid,u.username,r.id,r.NAME rname
      FROM t_user u
      LEFT JOIN t_user_role ur
      ON u.id=ur.userid
      LEFT JOIN t_role r
      ON ur.roleid=r.id
      WHERE u.username=#{username}
    </select>

    <select id="findPermsByRoleId" parameterType="String" resultType="Perms">
      SELECT p.id,p.NAME,p.url,r.NAME
      FROM t_role r
      LEFT JOIN t_role_perms rp
      ON r.id=rp.roleid
      LEFT JOIN t_perms p ON rp.permsid=p.id
      WHERE r.id=#{id}
    </select>

</mapper>

2.Service接口

  • 根据用户名查询所有角色
    User findRolesByUserName(String username);
  • 根据角色id查询权限集合
    List findPermsByRoleId(String id);
public interface UserService {

    //注册用户方法
    void register(User user);

    // 登录验证数据库用户
    User findByUserName(String username);

    //根据用户名查询所有角色
    User findRolesByUserName(String username);
    //根据角色id查询权限集合
    List<Perms> findPermsByRoleId(String id);
}

3.Service实现

@Service("userServiceImpl")
@Transactional
public class UserServiceImpl implements UserService {


    @Autowired
    private UserDAO userDAO;

    @Override
    public User findByUserName(String username) {
        return userDAO.findByUserName(username);
    }

    @Override
    public List<Perms> findPermsByRoleId(String id) {
        return userDAO.findPermsByRoleId(id);
    }

    @Override
    public User findRolesByUserName(String username) {
        return userDAO.findRolesByUserName(username);
    }

    @Override
    public void register(User user) {
        //处理业务调用dao
        //1.生成随机盐
        String salt = SaltUtils.getSalt(8);
        //2.将随机盐保存到数据
        user.setSalt(salt);
        //3.明文密码进行md5 + salt + hash散列
        Md5Hash md5Hash = new Md5Hash(user.getPassword(), salt, 1024);
        user.setPassword(md5Hash.toHex());
        userDAO.save(user);

    }
}

4.修改自定义CustomerRealm

在这里插入图片描述

public class CustomerRealm extends AuthorizingRealm {
    //授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principal) {
        //获取身份信息
        String primaryPrincipal = (String) principal.getPrimaryPrincipal();
        System.out.println("调用授权验证"+primaryPrincipal);
        //根據主身份信息获取角色和权限信息
        //在工厂工具类ApplicationContextUtils中获取service对象,默认策略是首字母小写,获取我们的userServiceImpl对象
        UserService userService = (UserService) ApplicationContextUtils.getBean("userServiceImpl");
        //根據主身份信息获取角色和权限信息实现
        User user = userService.findRolesByUserName(primaryPrincipal);
        //判断健壮性不为空
        if (!CollectionUtils.isEmpty(user.getRoles())){
            //创建一个身份权限信息
            SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
            //遍历集合里面的roles
            user.getRoles().forEach(role -> {
                //把遍历后的role角色添加到创建的身份权限信息对象
                simpleAuthorizationInfo.addRole(role.getName());

                //权限信息
                List<Perms> perms = userService.findPermsByRoleId(role.getId());
                if(!CollectionUtils.isEmpty(perms)){
                    perms.forEach(perm->{
                        simpleAuthorizationInfo.addStringPermission(perm.getName());
                    });
                }
            });
            //返回创建的身份权限信息对象
            return simpleAuthorizationInfo;
        }

        return null;
    }


    //认证
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        //伪代码数据
        //获取到前台传输的token
        String principal = (String) token.getPrincipal();
        //在工厂工具类ApplicationContextUtils中获取service对象,默认策略是首字母小写,获取我们的userServiceImpl对象
        UserService userService = (UserService) ApplicationContextUtils.getBean("userServiceImpl");
        //调用UserService
        User user = userService.findByUserName(principal);
//      旧版伪代码
//        if ("xiaozhang".equals(principal)){
//            //成功返回创建的对象信息
//            return new SimpleAuthenticationInfo(principal,"123",this.getName());
//        }
        //判断
        //第一个参数用户名
        //第二个参数密码
        //第三个参数随机盐
        //第四个参数是Realm的名字
        if (!ObjectUtils.isEmpty(user)){
            return new SimpleAuthenticationInfo(user.getUsername(),user.getPassword(), ByteSource.Util.bytes(user.getSalt()),this.getName());
        }

        return null;
    }
}

启动测试结果

在这里插入图片描述
在这里插入图片描述

—end

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值