分布式shiro权限验证 一

这是我参与更文挑战的第11天,活动详情查看: 更文挑战

立个Flag每天写点东西,坚持下去。

分布式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代码:gitee.com/tg_seahorse…

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享