分布式 shiro 权限验证
- 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 加入
还未添加个人简介










评论