Springboot shiro (二) 登录验证,角色和权限验证

使用场景:

  • 用户登录验证:
    在登录界面,需要对用户名和密码进行验证,验证通过后跳转到指定页面。验证不通过提示登录信息错误。

原理 管家服务, 如果shiro有感情,会发生以下事情:
1、 身份认证:那小伙在登录,你把那小伙的身份证给我,辨认、放行、驱赶的事交给我了
2、角色权限: 那小伙已经进大厅了,要去办公室拿东西,你把小伙的工作证给我,我去看那小伙是不是随意乱串呢。

  • 目录
    1、创建微服务,pom.xml添加引用
    2、当有用户登录的时候,将该用户的账号密码,角色、权限准备告诉shiro
    3、加shrio好友,把要刚定义的告知信息注入shiro
    4、给要限制访问的资源关门上锁
    5、枯燥的测试准备工作
    6、测试

  • 场景模拟:
    当看完文档一脸懵的时候,就该动手操作了。简单粗暴的操作完后,再去看文档…

    ****************************** 开始简单粗暴的场景模拟*****************************************

  • 第一步 创建微服务,pom.xml添加引用

        <!--web依赖,基础依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!--引入shiro依赖-->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.2.2</version>
        </dependency>

        <!--引入thymeleaf,作为web服务使用-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>

        <!--引入jpa,连接mysql-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>

        <!--mysql的java连接驱动-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
  • 第二步 当有用户登录的时候,将该用户的账号密码,角色、权限等告诉shiro
    注:要注入到框架,shiro也收不到信息
    1、继承AuthorizingRealm, 实现这两个方法
    doGetAuthenticationInfo(AuthenticationToken token)
    翻译:那小伙在登录,他已经给我说了他的身份证信息(参数AuthenticationToken),你把那小伙的身份证复印件给我,辨认、放行、驱赶的事交给我了

    doGetAuthorizationInfo(PrincipalCollection principalCollection)
    翻译:刚才那小伙已经进大厅了,我有他的身份证复印件(参数principalCollection),现在他要去办公室拿东西,你把小伙的工作证给我,我去看那小伙是不是随意乱串呢

doGetAuthenticationInfo(AuthenticationToken token)
该方法主要是用来验证身份和密码的。实现过程很简单,我们只需查询账户密码,交给shiro就行。
账号验证原理:在登陆输入用户名和密码时,shiro已经接管了用户名和密码。在此处只需取出用户名和密码,交给shiro即可,下面几个骚操作是框架自己完成的。不用实现
1、 框架会自动校验账户的正确性。
2、 如果正确,shrio会自动控制页面跳转到成功页面。
3、 如果不正确,shiro会将向/login发一个post请求,里面包含了错误信息,可以定制修改后返回给前端
.
.
doGetAuthorizationInfo(PrincipalCollection principalCollection)
实现方式主要是告诉shiro,当前登录的账号属于什么角色,有哪些权限。
此方法调用 hasRole,hasPermission的时候才会进行回调.

2、上代码,就是这么枯燥
package com.shiro.Service.shiro;

import com.shiro.Service.UserService;
import com.shiro.entity.SysPermission;
import com.shiro.entity.SysRole;
import com.shiro.entity.UserInfo;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
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;

/**
 * 自定义验证规则
 */
public class MyShiroRealm extends AuthorizingRealm {

   @Autowired
   private UserService userService;

   /**
    * 此方法调用封装角色和权限信息
    * 把当前登录用户的角色信息和权限信息告诉shiro
    * @param principalCollection
    * @return
    */
   @Override
   protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
      System.out.println("访问权限认证,此处调用了doGetAuthorizationInfo");
      SimpleAuthorizationInfo author = new SimpleAuthorizationInfo();
      UserInfo userInfo = (UserInfo) principalCollection.getPrimaryPrincipal();

      /**
       * 设置权限和角色信息
       */
      for(SysRole role:userInfo.getRoleList()){
         author.addRole(role.getRole());
         for(SysPermission p:role.getPermissions()){
            author.addStringPermission(p.getPermission());
         }
      }

      return author;
   }

   /**
    * 告诉shiro,名称是token的这个用户的账号和密码信息
    * @param token
    * @return
    * @throws AuthenticationException
    */
   @Override
   protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws
      AuthenticationException
   {
      //获取用户的输入的账号
      String username = (String)token.getPrincipal();
      //根据用户名查询用户基本信息,这个是查询数据库,不想查询只做演示的话此处造点假数据就行了
      UserInfo userInfo = userService.findByUsername(username);

      if(userInfo == null) {
         return null;
      }

      //根据用户名查询权限信息,并封装在用户信息中,此时未实现
      /*
       * 获取权限信息:这里没有进行实现,
       * 请自行根据UserInfo,Role,Permission进行实现;
       * 获取之后可以在前端for循环显示所有链接;
       */
      //userInfo.setPermissions(userService.findPermissions(user));

      SimpleAuthenticationInfo authInfo = new SimpleAuthenticationInfo(userInfo, //用户名
         userInfo.getPassword(), //密码
         ByteSource.Util.bytes(userInfo.getCredentialsSalt()),
         //salt=username+salt
        getName() );
      return authInfo;
   }
}

  • 第三步 加shrio好友,把要刚定义的告知信息注入shiro
    粗暴上代码
package com.shiro.config;

import com.shiro.Service.shiro.MyShiroRealm;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.LinkedHashMap;
import java.util.Map;

@Configuration
public class ShiroConfig {

   @Bean
   public MyShiroRealm myShiroRealm(){
      return new MyShiroRealm();
   }

   @Bean
   public SecurityManager sercurityManager(){
      DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
      defaultWebSecurityManager.setRealm(myShiroRealm());
      return defaultWebSecurityManager;
   }

   /**
    * 定义账号密码验证时,需要控制的路由跳转
    * 验证成功的导向和验证失败的导向
    * @param securityManager
    * @return
    */
   @Bean
   public ShiroFilterFactoryBean filterFactoryBean(SecurityManager securityManager){
      /**
       * 准备拦截器的拦截路径
       * 需要保持拦截顺序,所以用linkedHashMap,一般将/**放在最下面
       */
      Map<String, String> filterMap = new LinkedHashMap<>();
      //shiro已经内部实现了,不用关注这个页面
      filterMap.put("/logout", "logout");
      //authc:所有url都必须认证通过才可以访问; anon:所有url都都可以匿名访问
      filterMap.put("/**", "authc");

      ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
      shiroFilterFactoryBean.setSecurityManager(securityManager);
      //需要登录的页面
      shiroFilterFactoryBean.setLoginUrl("/login");
      //登录成功的页面
      shiroFilterFactoryBean.setSuccessUrl("/index");
      //登录失败的页面
      shiroFilterFactoryBean.setUnauthorizedUrl("/error");
      shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);
      return shiroFilterFactoryBean;
   }

   /**
    * 访问资源权限的配置
    * @param securityManager
    * @return
    */
   @Bean
   public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
      AuthorizationAttributeSourceAdvisor source = new AuthorizationAttributeSourceAdvisor();
      source.setSecurityManager(securityManager);
      return source;
   }

}

  • 第四步 给角色可访问的资源关门上锁
    就是要限制访问的资源上添加注解 @RequiresPermissions(“userInfo:add”)

以上的四步已经完成了技术问题。剩下的就是枯燥的测试过程,如果不想连数据库,可以造假数据直接测

  • 1、准备mysql数据库,选个没用的数据库执行下面sql

-- ----------------------------
-- Table structure for sys_permission
-- ----------------------------
DROP TABLE IF EXISTS `sys_permission`;
CREATE TABLE `sys_permission` (
  `id` bigint(20) NOT NULL,
  `available` bit(1) DEFAULT NULL,
  `name` varchar(255) DEFAULT NULL,
  `parent_id` bigint(20) DEFAULT NULL,
  `parent_ids` varchar(255) DEFAULT NULL,
  `permission` varchar(255) DEFAULT NULL,
  `resource_type` enum('menu','button') DEFAULT NULL,
  `url` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC;

-- ----------------------------
-- Records of sys_permission
-- ----------------------------
INSERT INTO `sys_permission` VALUES ('1', '', '用户管理', '0', '0/', 'userInfo:view', 'menu', 'userInfo/userList');
INSERT INTO `sys_permission` VALUES ('2', '', '用户添加', '1', '0/1', 'userInfo:add', 'button', 'userInfo/userAdd');
INSERT INTO `sys_permission` VALUES ('3', '', '用户删除', '1', '0/1', 'userInfo:del', 'button', 'userInfo/userDel');

-- ----------------------------
-- Table structure for sys_role
-- ----------------------------
DROP TABLE IF EXISTS `sys_role`;
CREATE TABLE `sys_role` (
  `id` bigint(20) NOT NULL,
  `available` bit(1) DEFAULT NULL,
  `description` varchar(255) DEFAULT NULL,
  `role` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC;

-- ----------------------------
-- Records of sys_role
-- ----------------------------
INSERT INTO `sys_role` VALUES ('1', '', '管理员', 'admin');
INSERT INTO `sys_role` VALUES ('2', '', 'VIP会员', 'vip');

-- ----------------------------
-- Table structure for sys_role_permission
-- ----------------------------
DROP TABLE IF EXISTS `sys_role_permission`;
CREATE TABLE `sys_role_permission` (
  `role_id` bigint(20) NOT NULL,
  `permission_id` bigint(20) NOT NULL,
  KEY `FKomxrs8a388bknvhjokh440waq` (`permission_id`),
  KEY `FK9q28ewrhntqeipl1t04kh1be7` (`role_id`),
  CONSTRAINT `FK9q28ewrhntqeipl1t04kh1be7` FOREIGN KEY (`role_id`) REFERENCES `sys_role` (`id`),
  CONSTRAINT `FKomxrs8a388bknvhjokh440waq` FOREIGN KEY (`permission_id`) REFERENCES `sys_permission` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC;

-- ----------------------------
-- Records of sys_role_permission
-- ----------------------------
INSERT INTO `sys_role_permission` VALUES ('1', '1');
INSERT INTO `sys_role_permission` VALUES ('1', '2');

-- ----------------------------
-- Table structure for sys_user_role
-- ----------------------------
DROP TABLE IF EXISTS `sys_user_role`;
CREATE TABLE `sys_user_role` (
  `uid` bigint(20) NOT NULL,
  `role_id` bigint(20) NOT NULL,
  KEY `FKhh52n8vd4ny9ff4x9fb8v65qx` (`role_id`),
  KEY `FKgkmyslkrfeyn9ukmolvek8b8f` (`uid`),
  CONSTRAINT `FKgkmyslkrfeyn9ukmolvek8b8f` FOREIGN KEY (`uid`) REFERENCES `user_info` (`uid`),
  CONSTRAINT `FKhh52n8vd4ny9ff4x9fb8v65qx` FOREIGN KEY (`role_id`) REFERENCES `sys_role` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC;

-- ----------------------------
-- Records of sys_user_role
-- ----------------------------
INSERT INTO `sys_user_role` VALUES ('1', '1');

-- ----------------------------
-- Table structure for user_info
-- ----------------------------
DROP TABLE IF EXISTS `user_info`;
CREATE TABLE `user_info` (
  `uid` bigint(20) NOT NULL,
  `name` varchar(255) DEFAULT NULL,
  `password` varchar(255) DEFAULT NULL,
  `salt` varchar(255) DEFAULT NULL,
  `state` tinyint(4) NOT NULL,
  `username` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`uid`),
  UNIQUE KEY `UK_f2ksd6h8hsjtd57ipfq9myr64` (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC;

-- ----------------------------
-- Records of user_info
-- ----------------------------
INSERT INTO `user_info` VALUES ('1', '管理员', '123456', 'd3c59d25033dbf980d29554025c23a75', '8', 'admin');

枯燥结束,开始测试
1、访问http://localhost:8001/userInfo/add 自动跳到了 http://localhost:8001/login
在这里插入图片描述
2、输入错误账号或密码有提示
在这里插入图片描述
3、登录成功后自动跳到了第一次想访问的页面 http://localhost:8001/userInfo/add
4、访问个没权限的页面,会跳到错误页面

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值