写点什么

分布式 shiro 权限验证

作者:Rubble
  • 2022 年 4 月 12 日
  • 本文字数:3462 字

    阅读完需:约 11 分钟

​ 对于非前后端分离的后台管理系统权限验证,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


@Configurationpublic 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/

用户头像

Rubble

关注

还未添加个人签名 2021.06.01 加入

还未添加个人简介

评论

发布
暂无评论
分布式shiro权限验证_4月日更_Rubble_InfoQ写作平台