分布式shiro权限验证

本文涉及的产品
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
云数据库 Tair(兼容Redis),内存型 2GB
简介: 对于非前后端分离的后台管理系统权限验证,shiro做为一个轻量级的权限验证框架,在很多以前的项目中会被使用。

分布式shiro权限验证

​ 对于非前后端分离的后台管理系统权限验证,shiro做为一个轻量级的权限验证框架,在很多以前的项目中会被使用。在新项目中一般会使用spring security,Spring提供的框架支持度较好。Shiro的常用注解 @RequiresPermissions @RequiresRoles @RequiresUser。

搭建shiro项目

引入依赖 shiro,reddisHttpSession, 此栗子用的thymeleaf

<dependency>
  <groupId>org.apache.shiro</groupId>
  <artifactId>shiro-spring-boot-web-starter</artifactId>
  <version>1.7.1</version>
</dependency>

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework.session</groupId>
  <artifactId>spring-session-data-redis</artifactId>
</dependency>

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

创建shiro的配置类, 必须注入 UserRealm,配置拦截的路径,密码验证HashedCredentialsMatcher

@Configuration
public class ShiroConfig {

  @Bean
  public UserRealm userRealm() {
    UserRealm userRealm = new UserRealm();
    userRealm.setCredentialsMatcher(this.credentialsMatcher());
    return userRealm;
  }

  @Bean
  public ShiroFilterChainDefinition shiroFilterChainDefinition() {
    DefaultShiroFilterChainDefinition chainDefinition = new DefaultShiroFilterChainDefinition();
    chainDefinition.addPathDefinition("/captcha", "anon");
    chainDefinition.addPathDefinition("/logout", "anon");
    chainDefinition.addPathDefinition("/layuiadmin/**", "anon");
    chainDefinition.addPathDefinition("/druid/**", "anon");
    chainDefinition.addPathDefinition("/api/**", "anon");
    chainDefinition.addPathDefinition("/login", "anon");
    chainDefinition.addPathDefinition("/**", "authc");
    return chainDefinition;
  }

  @Bean
  public HashedCredentialsMatcher credentialsMatcher() {
    HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
    credentialsMatcher.setHashAlgorithmName("SHA-256");
    credentialsMatcher.setStoredCredentialsHexEncoded(false);
    credentialsMatcher.setHashIterations(1024);
    return credentialsMatcher;
  }

  @Bean
  public SessionsSecurityManager securityManager() {
    DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
    securityManager.setRealm(this.userRealm());
    return securityManager;
  }
}

UserRealm类实现用户的认证及授权两个接口,可以从数据库获取用户账号信息进行验证登录,获取权限信息设置permissions、role。

public class UserRealm extends AuthorizingRealm {

  @Override
  protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
    User user = (User)SecurityUtils.getSubject().getPrincipal();
    SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
    Set<String> roles = new HashSet();
    Set<String> permissions = new HashSet();
    if ("admin".equals(user.getUserName())) {
      roles.add("admin");
      permissions.add("op:write");
    } else {
      roles.add("user");
      permissions.add("op:read");
    }

    authorizationInfo.setRoles(roles);
    authorizationInfo.setStringPermissions(permissions);
    return authorizationInfo;
  }

  @Override
  protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
    String username = (String)authenticationToken.getPrincipal();
    User user = new User();
    user.setUserName("admin");
    String password = "123456";
    String salt = "salt";
    int hashIterations = 1024;
    String encodedPassword = (new SimpleHash("SHA-256", password, Util.bytes(salt), hashIterations)).toBase64();
    user.setPassword(encodedPassword);
    SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(user, user.getPassword(), Util.bytes(salt), this.getName());
    return authenticationInfo;
  }

写一个简单的页面

登录页面login.html 及index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Shiro Login</title>
</head>
<body>
<h3>Home Login</h3>
<div>port: <span>[[${#httpServletRequest.getServerPort()}]]</span></div>
<div>session: <span>[[${#httpServletRequest.getSession().getId()}]]</span></div>
<div>time: <span>[[${#dates.format(new java.util.Date().getTime(), 'yyyy-MM-dd hh:mm:ss')}]]</span></div>
<form method="post" action="/login">
    <div><label for="userName">UserName:</label><input type="text" name="userName" value="admin" id="userName"></div>
    <div><label for="password">Password:</label><input type="password" name="password" value="123456" id="password"></div>
    <div><input type="submit" value="Submit"></div>
</form>

</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Shiro Session</title>
</head>
<body>
<h3>Shiro Session home</h3>
<div>port: <span>[[${#httpServletRequest.getServerPort()}]]</span></div>
<div>session: <span>[[${#httpServletRequest.getSession().getId()}]]</span></div>
<div>time: <span>[[${#dates.format(new java.util.Date().getTime(), 'yyyy-MM-dd hh:mm:ss')}]]</span></div>

</body>
</html>

登录逻辑进行处理

@RequestMapping(value = "/login",method = {RequestMethod.GET,RequestMethod.POST})
public String doLogin (@RequestParam(required = false) String userName, @RequestParam(required = false) String password,
    @RequestParam(required = false) String easyCaptcha, HttpServletRequest request) {
  Object principal = SecurityUtils.getSubject().getPrincipal();
  if (principal != null) {
    return "index";
  }
  if (!StrUtil.isEmpty(userName) && !StrUtil.isEmpty(password)) {
    try {
      UsernamePasswordToken token = new UsernamePasswordToken(userName, password);
      SecurityUtils.getSubject().login(token);
      HashMap<String, String> map = new HashMap(16);
      map.put("access_token", "1111111111111111111");
      return "index";
    } catch (IncorrectCredentialsException var7) {
      log.info("密码错误 {}", var7.getMessage());
    } catch (UnknownAccountException var8) {
      log.info("账号不存在 {}", var8.getMessage());
    } catch (LockedAccountException var9) {
      log.info("账号被锁定 {}", var9.getMessage());
    } catch (ExcessiveAttemptsException var10) {
      log.info("操作频繁,请稍后再试 {}", var10.getMessage());
    } catch (Exception var11) {
      log.error("登录异常 {}", var11.getMessage(), var11);
    }
  }
  return "login";
}

至此shiro的系统已经完成。基于shiro-spring-boot-web-starter的项目就是如此简单。

下面增加分布式,在项目启动类上添加注解,注入注册中心,启用redisHttpSession,会解析cookie的sessionId并将数据存放的reids中,以实现分布式session.

@EnableRedisHttpSession
@EnableDiscoveryClient
@SpringBootApplication

将项目加入网关通过网关来访问,登录之后即可实现分布式session。
需要注意的是session的作用域,需要在相同的域名domain下才有效。

对于前后端分离的项目,登录后拿到cookie, 每次请求cookie携带sessionId也可以实现shiro的分布式session.

gitee代码:https://gitee.com/tg_seahorse/paw-demos/tree/paw-authorize/

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
存储 缓存 NoSQL
Shiro 解决分布式 Session
在分布式系统中,会话管理是一个重要的问题。Shiro框架提供了一种解决方案,通过其会话管理组件来处理分布式会话。本文演示通过RedisSessionManager解决分布式会话问题。
109 0
|
6月前
|
Linux 数据安全/隐私保护 Windows
分布式系统详解--Linux(权限)
分布式系统详解--Linux(权限)
49 0
|
消息中间件 监控 Cloud Native
基于SpringCloud体系实现的一套支持云原生的分布式微服务架构,提供OAuth2/JWT权限认证、分布式事务、灰度、限流、链路追踪等功能,支持Docker容器化部署、镜像交付、K8S容器编排
lion是基于Spring Cloud体系实现的一套支持云原生的分布式微服务架构,为了让中小型公司解决当下技术瓶颈,快速将现有应用服务架构拆分改造为分布式微服务架构,进入 All-in-Cloud 时代,只需在本架构上进行相关业务开发即可,大大减少了分布式微服务架构的门槛,仅在本框架上做&quot;减法&quot;的目的,使架构师及开发人员不必过多的关注架构本身,只需专注于业务开发
基于SpringCloud体系实现的一套支持云原生的分布式微服务架构,提供OAuth2/JWT权限认证、分布式事务、灰度、限流、链路追踪等功能,支持Docker容器化部署、镜像交付、K8S容器编排
|
JSON 安全 Java
分布式整合之验证认证的过滤器|学习笔记
快速学习分布式整合之验证认证的过滤器
分布式整合之验证认证的过滤器|学习笔记
|
API 数据库 数据安全/隐私保护
分布式系统中,权限设计实践
采用合理的策略去管理资源的权限并不是一件简单的事,通常随着业务和系统的不断扩展,对权限体系都会带来直接的影响,所以在做结构设计时,需要相对复杂但又要避免过度复杂。
486 0
分布式系统中,权限设计实践
|
安全 Oracle 容灾
权威认可!OceanBase 通过分布式数据库金融标准验证
近日,经北京国家金融科技认证中心的严格测试评定,北京奥星贝斯科技有限公司OceanBase 数据库产品完成了“分布式数据库金融标准符合性验证”全部测试项目,并获得高分,证明了 OceanBase 数据库满足“分布式数据库金融规范”要求。 该测评从基本功能、兼容能力、管理能力、高可用能力、分布式能力、性能和安全能力等方面着手,验证分布式数据库在金融核心场景是否满足标准要求。
360 0
|
NoSQL Redis
基于shiro实现session持久化和分布式共享(3)
基于shiro实现session持久化和分布式共享(3)
186 0
基于shiro实现session持久化和分布式共享(3)
|
存储 缓存 NoSQL
基于shiro实现session持久化和分布式共享(2)
基于shiro实现session持久化和分布式共享(2)
360 0
基于shiro实现session持久化和分布式共享(2)
|
缓存
基于shiro实现session持久化和分布式共享(1)
基于shiro实现session持久化和分布式共享(1)
248 0
基于shiro实现session持久化和分布式共享(1)