关于shiro:SpringBoot-整合Shiro实现动态权限加载更新Session共享单点登录

3次阅读

共计 22434 个字符,预计需要花费 57 分钟才能阅读完成。

我的公众号:MarkerHub,网站:https://markerhub.com

更多精选文章请点击:Java 笔记大全.md

小 Hub 领读:

有源码的教程,不会的同学下载源码,依据教程学一下哈~


作者:Sans_

juejin.im/post/5d087d605188256de9779e64

一. 阐明

Shiro 是一个平安框架, 我的项目中次要用它做认证, 受权, 加密, 以及用户的会话治理, 尽管 Shiro 没有 SpringSecurity 性能更丰盛, 然而它轻量, 简略, 在我的项目中通常业务需要 Shiro 也都能胜任.

二. 我的项目环境

  • MyBatis-Plus 版本: 3.1.0
  • SpringBoot 版本: 2.1.5
  • JDK 版本: 1.8
  • Shiro 版本: 1.4
  • Shiro-redis 插件版本: 3.1.0

数据表 (SQL 文件在我的项目中): 数据库中测试号的明码进行了加密, 明码皆为 123456

Maven 依赖如下:

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <!-- AOP 依赖, 肯定要加, 否则权限拦挡验证不失效 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
        <!-- lombok 插件 -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <!-- Redis -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
        </dependency>
        <!-- mybatisPlus 外围库 -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.1.0</version>
        </dependency>
        <!-- 引入阿里数据库连接池 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.6</version>
        </dependency>
        <!-- Shiro 外围依赖 -->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.4.0</version>
        </dependency>
        <!-- Shiro-redis 插件 -->
        <dependency>
            <groupId>org.crazycake</groupId>
            <artifactId>shiro-redis</artifactId>
            <version>3.1.0</version>
        </dependency>
        <!-- StringUitlS 工具 -->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.5</version>
        </dependency>
</dependencies>

配置如下:

# 配置端口
server:
  port: 8764
spring:
  # 配置数据源
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/my_shiro?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&useSSL=false
    username: root
    password: root
    type: com.alibaba.druid.pool.DruidDataSource
  # Redis 数据源
  redis:
    host: localhost
    port: 6379
    timeout: 6000
    password: 123456
    jedis:
      pool:
        max-active: 1000  # 连接池最大连接数(应用负值示意没有限度)max-wait: -1      # 连接池最大阻塞等待时间(应用负值示意没有限度)max-idle: 10      # 连接池中的最大闲暇连贯
        min-idle: 5       # 连接池中的最小闲暇连贯
# mybatis-plus 相干配置
mybatis-plus:
  # xml 扫描,多个目录用逗号或者分号分隔(通知 Mapper 所对应的 XML 文件地位)mapper-locations: classpath:mapper/*.xml
  # 以下配置均有默认值, 能够不设置
  global-config:
    db-config:
      #主键类型 AUTO:"数据库 ID 自增" INPUT:"用户输出 ID",ID_WORKER:"全局惟一 ID (数字类型惟一 ID)", UUID:"全局惟一 ID UUID";
      id-type: auto
      #字段策略 IGNORED:"疏忽判断"  NOT_NULL:"非 NULL 判断")  NOT_EMPTY:"非空判断"
      field-strategy: NOT_EMPTY
      #数据库类型
      db-type: MYSQL
  configuration:
    # 是否开启主动驼峰命名规定映射: 从数据库列名到 Java 属性驼峰命名的相似映射
    map-underscore-to-camel-case: true
    # 如果查问后果中蕴含空值的列,则 MyBatis 在映射的时候,不会映射这个字段
    call-setters-on-nulls: true
    # 这个配置会将执行的 sql 打印进去,在开发或测试的时候能够用
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

二. 编写我的项目根底类

用户实体, Dao,Service 等在这里省略, 请参考源码

编写 Exception 类来解决 Shiro 权限拦挡异样

创立 SHA256Util 加密工具

创立 Spring 工具

/**
 * @Description Spring 上下文工具类
 * @Author Sans
 * @CreateTime 2019/6/17 13:40
 */
@Component
public class SpringUtil implements ApplicationContextAware {
    private static ApplicationContext context;
    /**
     * Spring 在 bean 初始化后会判断是不是 ApplicationContextAware 的子类
     * 如果该类是,setApplicationContext() 办法, 会将容器中 ApplicationContext 作为参数传入进去
     * @Author Sans
     * @CreateTime 2019/6/17 16:58
     */
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {context = applicationContext;}
    /**
     * 通过 Name 返回指定的 Bean
     * @Author Sans
     * @CreateTime 2019/6/17 16:03
     */
    public static <T> T getBean(Class<T> beanClass) {return context.getBean(beanClass);
    }
}

创立 Shiro 工具

/**
 * @Description Shiro 工具类
 * @Author Sans
 * @CreateTime 2019/6/15 16:11
 */
public class ShiroUtils {

    /** 公有结构器 **/
    private ShiroUtils(){}

    private static RedisSessionDAO redisSessionDAO = SpringUtil.getBean(RedisSessionDAO.class);

    /**
     * 获取以后用户 Session
     * @Author Sans
     * @CreateTime 2019/6/17 17:03
     * @Return SysUserEntity 用户信息
     */
    public static Session getSession() {return SecurityUtils.getSubject().getSession();}

    /**
     * 用户登出
     * @Author Sans
     * @CreateTime 2019/6/17 17:23
     */
    public static void logout() {SecurityUtils.getSubject().logout();}

    /**
    * 获取以后用户信息
    * @Author Sans
    * @CreateTime 2019/6/17 17:03
    * @Return SysUserEntity 用户信息
    */
    public static SysUserEntity getUserInfo() {return (SysUserEntity) SecurityUtils.getSubject().getPrincipal();
    }

    /**
     * 删除用户缓存信息
     * @Author Sans
     * @CreateTime 2019/6/17 13:57
     * @Param  username  用户名称
     * @Param  isRemoveSession 是否删除 Session
     * @Return void
     */
    public static void deleteCache(String username, boolean isRemoveSession){
        // 从缓存中获取 Session
        Session session = null;
        Collection<Session> sessions = redisSessionDAO.getActiveSessions();
        SysUserEntity sysUserEntity;
        Object attribute = null;
        for(Session sessionInfo : sessions){
            // 遍历 Session, 找到该用户名称对应的 Session
            attribute = sessionInfo.getAttribute(DefaultSubjectContext.PRINCIPALS_SESSION_KEY);
            if (attribute == null) {continue;}
            sysUserEntity = (SysUserEntity) ((SimplePrincipalCollection) attribute).getPrimaryPrincipal();
            if (sysUserEntity == null) {continue;}
            if (Objects.equals(sysUserEntity.getUsername(), username)) {session=sessionInfo;}
        }
        if (session == null||attribute == null) {return;}
        // 删除 session
        if (isRemoveSession) {redisSessionDAO.delete(session);
        }
        // 删除 Cache,在拜访受限接口时会从新受权
        DefaultWebSecurityManager securityManager = (DefaultWebSecurityManager) SecurityUtils.getSecurityManager();
        Authenticator authc = securityManager.getAuthenticator();
        ((LogoutAware) authc).onLogout((SimplePrincipalCollection) attribute);
    }
}

创立 Shiro 的 SessionId 生成器

三. 编写 Shiro 外围类

创立 Realm 用于受权和认证

/**
 * @Description Shiro 权限匹配和账号密码匹配
 * @Author Sans
 * @CreateTime 2019/6/15 11:27
 */
public class ShiroRealm extends AuthorizingRealm {
    @Autowired
    private SysUserService sysUserService;
    @Autowired
    private SysRoleService sysRoleService;
    @Autowired
    private SysMenuService sysMenuService;
    /**
     * 受权权限
     * 用户进行权限验证时候 Shiro 会去缓存中找, 如果查不到数据, 会执行这个办法去查权限, 并放入缓存中
     * @Author Sans
     * @CreateTime 2019/6/12 11:44
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
        SysUserEntity sysUserEntity = (SysUserEntity) principalCollection.getPrimaryPrincipal();
        // 获取用户 ID
        Long userId =sysUserEntity.getUserId();
        // 这里能够进行受权和解决
        Set<String> rolesSet = new HashSet<>();
        Set<String> permsSet = new HashSet<>();
        // 查问角色和权限 (这里依据业务自行查问)
        List<SysRoleEntity> sysRoleEntityList = sysRoleService.selectSysRoleByUserId(userId);
        for (SysRoleEntity sysRoleEntity:sysRoleEntityList) {rolesSet.add(sysRoleEntity.getRoleName());
            List<SysMenuEntity> sysMenuEntityList = sysMenuService.selectSysMenuByRoleId(sysRoleEntity.getRoleId());
            for (SysMenuEntity sysMenuEntity :sysMenuEntityList) {permsSet.add(sysMenuEntity.getPerms());
            }
        }
        // 将查到的权限和角色别离传入 authorizationInfo 中
        authorizationInfo.setStringPermissions(permsSet);
        authorizationInfo.setRoles(rolesSet);
        return authorizationInfo;
    }

    /**
     * 身份认证
     * @Author Sans
     * @CreateTime 2019/6/12 12:36
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        // 获取用户的输出的账号.
        String username = (String) authenticationToken.getPrincipal();
        // 通过 username 从数据库中查找 User 对象,如果找到进行验证
        // 理论我的项目中, 这里能够依据理论状况做缓存, 如果不做,Shiro 本人也是有工夫距离机制,2 分钟内不会反复执行该办法
        SysUserEntity user = sysUserService.selectUserByName(username);
        // 判断账号是否存在
        if (user == null) {throw new AuthenticationException();
        }
        // 判断账号是否被解冻
        if (user.getState()==null||user.getState().equals("PROHIBIT")){throw new LockedAccountException();
        }
        // 进行验证
        SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(
                user,                                  // 用户名
                user.getPassword(),                    // 明码
                ByteSource.Util.bytes(user.getSalt()), // 设置盐值
                getName());
        // 验证胜利开始踢人 (革除缓存和 Session)
        ShiroUtils.deleteCache(username,true);
        return authenticationInfo;
    }
}

创立 SessionManager 类

创立 ShiroConfig 配置类

/**
 * @Description Shiro 配置类
 * @Author Sans
 * @CreateTime 2019/6/10 17:42
 */
@Configuration
public class ShiroConfig {

    private final String CACHE_KEY = "shiro:cache:";
    private final String SESSION_KEY = "shiro:session:";
    private final int EXPIRE = 1800;

    //Redis 配置
    @Value("${spring.redis.host}")
    private String host;
    @Value("${spring.redis.port}")
    private int port;
    @Value("${spring.redis.timeout}")
    private int timeout;
    @Value("${spring.redis.password}")
    private String password;

    /**
     * 开启 Shiro-aop 注解反对
     * @Attention 应用代理形式所以须要开启代码反对
     * @Author Sans
     * @CreateTime 2019/6/12 8:38
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }

    /**
     * Shiro 根底配置
     * @Author Sans
     * @CreateTime 2019/6/12 8:42
     */
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactory(SecurityManager securityManager){ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
        // 留神过滤器配置程序不能颠倒
        // 配置过滤: 不会被拦挡的链接
        filterChainDefinitionMap.put("/static/**", "anon");
        filterChainDefinitionMap.put("/userLogin/**", "anon");
        filterChainDefinitionMap.put("/**", "authc");
        // 配置 shiro 默认登录界面地址,前后端拆散中登录界面跳转应由前端路由管制,后盾仅返回 json 数据
        shiroFilterFactoryBean.setLoginUrl("/userLogin/unauth");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
        return shiroFilterFactoryBean;
    }

    /**
     * 平安管理器
     * @Author Sans
     * @CreateTime 2019/6/12 10:34
     */
    @Bean
    public SecurityManager securityManager() {DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        // 自定义 Ssession 治理
        securityManager.setSessionManager(sessionManager());
        // 自定义 Cache 实现
        securityManager.setCacheManager(cacheManager());
        // 自定义 Realm 验证
        securityManager.setRealm(shiroRealm());
        return securityManager;
    }

    /**
     * 身份验证器
     * @Author Sans
     * @CreateTime 2019/6/12 10:37
     */
    @Bean
    public ShiroRealm shiroRealm() {ShiroRealm shiroRealm = new ShiroRealm();
        shiroRealm.setCredentialsMatcher(hashedCredentialsMatcher());
        return shiroRealm;
    }

    /**
     * 凭证匹配器
     * 将明码校验交给 Shiro 的 SimpleAuthenticationInfo 进行解决, 在这里做匹配配置
     * @Author Sans
     * @CreateTime 2019/6/12 10:48
     */
    @Bean
    public HashedCredentialsMatcher hashedCredentialsMatcher() {HashedCredentialsMatcher shaCredentialsMatcher = new HashedCredentialsMatcher();
        // 散列算法: 这里应用 SHA256 算法;
        shaCredentialsMatcher.setHashAlgorithmName(SHA256Util.HASH_ALGORITHM_NAME);
        // 散列的次数,比方散列两次,相当于 md5(md5(""));
        shaCredentialsMatcher.setHashIterations(SHA256Util.HASH_ITERATIONS);
        return shaCredentialsMatcher;
    }

    /**
     * 配置 Redis 管理器
     * @Attention 应用的是 shiro-redis 开源插件
     * @Author Sans
     * @CreateTime 2019/6/12 11:06
     */
    @Bean
    public RedisManager redisManager() {RedisManager redisManager = new RedisManager();
        redisManager.setHost(host);
        redisManager.setPort(port);
        redisManager.setTimeout(timeout);
        redisManager.setPassword(password);
        return redisManager;
    }

    /**
     * 配置 Cache 管理器
     * 用于往 Redis 存储权限和角色标识
     * @Attention 应用的是 shiro-redis 开源插件
     * @Author Sans
     * @CreateTime 2019/6/12 12:37
     */
    @Bean
    public RedisCacheManager cacheManager() {RedisCacheManager redisCacheManager = new RedisCacheManager();
        redisCacheManager.setRedisManager(redisManager());
        redisCacheManager.setKeyPrefix(CACHE_KEY);
        // 配置缓存的话要求放在 session 外面的实体类必须有个 id 标识
        redisCacheManager.setPrincipalIdFieldName("userId");
        return redisCacheManager;
    }

    /**
     * SessionID 生成器
     * @Author Sans
     * @CreateTime 2019/6/12 13:12
     */
    @Bean
    public ShiroSessionIdGenerator sessionIdGenerator(){return new ShiroSessionIdGenerator();
    }

    /**
     * 配置 RedisSessionDAO
     * @Attention 应用的是 shiro-redis 开源插件
     * @Author Sans
     * @CreateTime 2019/6/12 13:44
     */
    @Bean
    public RedisSessionDAO redisSessionDAO() {RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
        redisSessionDAO.setRedisManager(redisManager());
        redisSessionDAO.setSessionIdGenerator(sessionIdGenerator());
        redisSessionDAO.setKeyPrefix(SESSION_KEY);
        redisSessionDAO.setExpire(expire);
        return redisSessionDAO;
    }

    /**
     * 配置 Session 管理器
     * @Author Sans
     * @CreateTime 2019/6/12 14:25
     */
    @Bean
    public SessionManager sessionManager() {ShiroSessionManager shiroSessionManager = new ShiroSessionManager();
        shiroSessionManager.setSessionDAO(redisSessionDAO());
        return shiroSessionManager;
    }
}

四. 实现权限管制

Shiro 能够用代码或者注解来管制权限, 通常咱们应用注解管制, 不仅简略不便, 而且更加灵便. Shiro 注解一共有五个:

个别状况下咱们在我的项目中做权限管制, 应用最多的是 RequiresPermissions 和 RequiresRoles, 容许存在多个角色和权限, 默认逻辑是 AND, 也就是同时领有这些才能够拜访办法, 能够在注解中以参数的模式设置成 OR

示例

应用程序: Shiro 注解是存在程序的, 当多个注解在一个办法上的时候, 会一一查看, 晓得全副通过为止, 默认拦挡程序是: RequiresRoles->RequiresPermissions->RequiresAuthentication->
RequiresUser->RequiresGuest

示例

创立 UserRoleController 角色拦挡测试类

/**
 * @Description 角色测试
 * @Author Sans
 * @CreateTime 2019/6/19 11:38
 */
@RestController
@RequestMapping("/role")
public class UserRoleController {

    @Autowired
    private SysUserService sysUserService;
    @Autowired
    private SysRoleService sysRoleService;
    @Autowired
    private SysMenuService sysMenuService;
    @Autowired
    private SysRoleMenuService sysRoleMenuService;

    /**
     * 管理员角色测试接口
     * @Author Sans
     * @CreateTime 2019/6/19 10:38
     * @Return Map<String,Object> 返回后果
     */
    @RequestMapping("/getAdminInfo")
    @RequiresRoles("ADMIN")
    public Map<String,Object> getAdminInfo(){Map<String,Object> map = new HashMap<>();
        map.put("code",200);
        map.put("msg","这里是只有管理员角色能拜访的接口");
        return map;
    }

    /**
     * 用户角色测试接口
     * @Author Sans
     * @CreateTime 2019/6/19 10:38
     * @Return Map<String,Object> 返回后果
     */
    @RequestMapping("/getUserInfo")
    @RequiresRoles("USER")
    public Map<String,Object> getUserInfo(){Map<String,Object> map = new HashMap<>();
        map.put("code",200);
        map.put("msg","这里是只有用户角色能拜访的接口");
        return map;
    }

    /**
     * 角色测试接口
     * @Author Sans
     * @CreateTime 2019/6/19 10:38
     * @Return Map<String,Object> 返回后果
     */
    @RequestMapping("/getRoleInfo")
    @RequiresRoles(value={"ADMIN","USER"},logical = Logical.OR)
    @RequiresUser
    public Map<String,Object> getRoleInfo(){Map<String,Object> map = new HashMap<>();
        map.put("code",200);
        map.put("msg","这里是只有有 ADMIN 或者 USER 角色能拜访的接口");
        return map;
    }

    /**
     * 登出 (测试登出)
     * @Author Sans
     * @CreateTime 2019/6/19 10:38
     * @Return Map<String,Object> 返回后果
     */
    @RequestMapping("/getLogout")
    @RequiresUser
    public Map<String,Object> getLogout(){ShiroUtils.logout();
        Map<String,Object> map = new HashMap<>();
        map.put("code",200);
        map.put("msg","登出");
        return map;
    }
}

创立 UserMenuController 权限拦挡测试类

/**
 * @Description 权限测试
 * @Author Sans
 * @CreateTime 2019/6/19 11:38
 */
@RestController
@RequestMapping("/menu")
public class UserMenuController {

    @Autowired
    private SysUserService sysUserService;
    @Autowired
    private SysRoleService sysRoleService;
    @Autowired
    private SysMenuService sysMenuService;
    @Autowired
    private SysRoleMenuService sysRoleMenuService;

    /**
     * 获取用户信息汇合
     * @Author Sans
     * @CreateTime 2019/6/19 10:36
     * @Return Map<String,Object> 返回后果
     */
    @RequestMapping("/getUserInfoList")
    @RequiresPermissions("sys:user:info")
    public Map<String,Object> getUserInfoList(){Map<String,Object> map = new HashMap<>();
        List<SysUserEntity> sysUserEntityList = sysUserService.list();
        map.put("sysUserEntityList",sysUserEntityList);
        return map;
    }

    /**
     * 获取角色信息汇合
     * @Author Sans
     * @CreateTime 2019/6/19 10:37
     * @Return Map<String,Object> 返回后果
     */
    @RequestMapping("/getRoleInfoList")
    @RequiresPermissions("sys:role:info")
    public Map<String,Object> getRoleInfoList(){Map<String,Object> map = new HashMap<>();
        List<SysRoleEntity> sysRoleEntityList = sysRoleService.list();
        map.put("sysRoleEntityList",sysRoleEntityList);
        return map;
    }

    /**
     * 获取权限信息汇合
     * @Author Sans
     * @CreateTime 2019/6/19 10:38
     * @Return Map<String,Object> 返回后果
     */
    @RequestMapping("/getMenuInfoList")
    @RequiresPermissions("sys:menu:info")
    public Map<String,Object> getMenuInfoList(){Map<String,Object> map = new HashMap<>();
        List<SysMenuEntity> sysMenuEntityList = sysMenuService.list();
        map.put("sysMenuEntityList",sysMenuEntityList);
        return map;
    }

    /**
     * 获取所有数据
     * @Author Sans
     * @CreateTime 2019/6/19 10:38
     * @Return Map<String,Object> 返回后果
     */
    @RequestMapping("/getInfoAll")
    @RequiresPermissions("sys:info:all")
    public Map<String,Object> getInfoAll(){Map<String,Object> map = new HashMap<>();
        List<SysUserEntity> sysUserEntityList = sysUserService.list();
        map.put("sysUserEntityList",sysUserEntityList);
        List<SysRoleEntity> sysRoleEntityList = sysRoleService.list();
        map.put("sysRoleEntityList",sysRoleEntityList);
        List<SysMenuEntity> sysMenuEntityList = sysMenuService.list();
        map.put("sysMenuEntityList",sysMenuEntityList);
        return map;
    }

    /**
     * 增加管理员角色权限 (测试动静权限更新)
     * @Author Sans
     * @CreateTime 2019/6/19 10:39
     * @Param  username 用户 ID
     * @Return Map<String,Object> 返回后果
     */
    @RequestMapping("/addMenu")
    public Map<String,Object> addMenu(){
        // 增加管理员角色权限
        SysRoleMenuEntity sysRoleMenuEntity = new SysRoleMenuEntity();
        sysRoleMenuEntity.setMenuId(4L);
        sysRoleMenuEntity.setRoleId(1L);
        sysRoleMenuService.save(sysRoleMenuEntity);
        // 革除缓存
        String username = "admin";
        ShiroUtils.deleteCache(username,false);
        Map<String,Object> map = new HashMap<>();
        map.put("code",200);
        map.put("msg","权限增加胜利");
        return map;
    }
}

创立 UserLoginController 登录类

/**
 * @Description 用户登录
 * @Author Sans
 * @CreateTime 2019/6/17 15:21
 */
@RestController
@RequestMapping("/userLogin")
public class UserLoginController {

    @Autowired
    private SysUserService sysUserService;

    /**
     * 登录
     * @Author Sans
     * @CreateTime 2019/6/20 9:21
     */
    @RequestMapping("/login")
    public Map<String,Object> login(@RequestBody SysUserEntity sysUserEntity){Map<String,Object> map = new HashMap<>();
        // 进行身份验证
        try{
            // 验证身份和登陆
            Subject subject = SecurityUtils.getSubject();
            UsernamePasswordToken token = new UsernamePasswordToken(sysUserEntity.getUsername(), sysUserEntity.getPassword());
            // 验证胜利进行登录操作
            subject.login(token);
        }catch (IncorrectCredentialsException e) {map.put("code",500);
            map.put("msg","用户不存在或者明码谬误");
            return map;
        } catch (LockedAccountException e) {map.put("code",500);
            map.put("msg","登录失败,该用户已被解冻");
            return map;
        } catch (AuthenticationException e) {map.put("code",500);
            map.put("msg","该用户不存在");
            return map;
        } catch (Exception e) {map.put("code",500);
            map.put("msg","未知异样");
            return map;
        }
        map.put("code",0);
        map.put("msg","登录胜利");
        map.put("token",ShiroUtils.getSession().getId().toString());
        return map;
    }
    /**
     * 未登录
     * @Author Sans
     * @CreateTime 2019/6/20 9:22
     */
    @RequestMapping("/unauth")
    public Map<String,Object> unauth(){Map<String,Object> map = new HashMap<>();
        map.put("code",500);
        map.put("msg","未登录");
        return map;
    }
}

五. POSTMAN 测试

登录胜利后会返回 TOKEN, 因为是单点登录, 再次登陆的话会返回新的 TOKEN, 之前 Redis 的 TOKEN 就会生效了

当第一次拜访接口后咱们能够看到缓存中曾经有权限数据了, 在次访问接口的时候, Shiro 会间接去缓存中拿取权限, 留神拜访接口时候要设置申请头.

ADMIN 这个号当初没有 sys:info:all 这个权限的, 所以无法访问 getInfoAll 接口, 咱们要动态分配权限后, 要清掉缓存, 在拜访接口时候, Shiro 会去从新执行受权办法, 之后再次把权限和角色数据放入缓存中

拜访增加权限测试接口, 因为是测试, 我把减少权限的用户 ADMIN 写死在外面了, 权限增加后, 调用工具类清掉缓存, 咱们能够发现, Redis 中曾经没有缓存了

再次拜访 getInfoAll 接口, 因为缓存中没有数据, Shiro 会从新受权查问权限, 拦挡通过

六. 我的项目源码

https://gitee.com/liselotte/s…

https://github.com/xuyulong20…


(完)

举荐浏览

Java 笔记大全.md

太赞了,这个 Java 网站,什么我的项目都有!https://markerhub.com

这个 B 站的 UP 主,讲的 java 真不错!

太赞了!最新版 Java 编程思维能够在线看了!

正文完
 0