分布式shiro权限验证

举报
琴岛蛏子 发表于 2022/03/21 00:04:30 2022/03/21
【摘要】 分布式shiro权限验证​ 对于非前后端分离的后台管理系统权限验证,shiro做为一个轻量级的权限验证框架,在很多以前的项目中会被使用。在新项目中一般会使用spring security,Spring提供的框架支持度较好。Shiro的常用注解 @RequiresPermissions @RequiresRoles @RequiresUser。 搭建shiro项目引入依赖 shiro,r...

分布式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/

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。