Shiro简介这是一篇Shiro入门的博文,将整合Spring Boot快速入门Shiro。实现基本的认证、授权、密码加密功能
认证:也就是我们平时所说的登录,但认证不局限于账号密码登录,扫码、人脸识别、指纹等都可以算是认证
授权:不同的人拥有不同的权限,比如在后台管理系统中,管理员和普通用户看到的菜单是不一样的、有些资源普通用户只有读权限没有写权限等
Shiro简单来说就是一个安全框架,它可以帮助我们快速、容易的实现认证、授权的相关功能。
我们先了解一下Shiro的架构。
在Shiro中有三个重要的角色
- Subject:主体,代表了当前 “用户”,这个用户不一定是一个具体的人,与当前应用交互的任何东西都是 Subject,如网络爬虫,机器人等;即一个抽象概念;所有 Subject 都绑定到SecurityManager,与 Subject 的所有交互都会委托给 SecurityManager;可以把 Subject 认为是一个门面;SecurityManager 才是实际的执行者;
- SecurityManager: 安全管理器,即所有与安全有关的 *** 作都会与 SecurityManager 交互;且它管理着所有 Subject,如果学习过 SpringMVC,你可以把它看成 DispatcherServlet 前端控制器;
- Realm:Shiro 从 Realm 获取安全数据(如用户、角色、权限),就是说 SecurityManager 要验证用户身份,那么它需要从 Realm 获取相应的用户进行比较以确定用户身份是否合法;也需要从 Realm 得到用户相应的角色 / 权限进行验证用户是否能进行 *** 作;可以把 Realm 看成 DataSource,即安全数据源。
2、自定义一个UserRealm。还记得Realm是干嘛的吗?org.springframework.boot spring-boot-starter-weborg.apache.shiro shiro-spring-boot-starter1.8.0 org.springframework.boot spring-boot-starter-thymeleaf
// 作用类比Spring Security中的UserDetailsService public class UserRealm extends AuthorizingRealm { // 模拟数据库中的数据 private Map3、配置安全管理器和资源访问规则users = new HashMap<>(); { // xiaowang拥有俩种权限,所有最终结果是他所有的资源都可以访问 users.put("xiaowang", new User("xiaowang", password, "user:add,user:update")); users.put("xiaoming", new User("xiaoming", password, "user:update")); } // 类比Spring Security中的UserDetailsService的loadUserByUsername方法 // 根据username找到用户的信息 @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { System.out.println("执行了认证=>doGetAuthenticationInfo"); UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken; // 模拟从数据库中根据用户名查用户信息 User user = users.get(token.getUsername()); if (user == null) { throw new UnknownAccountException(); } // user信息最终会被放在上下文中,在授权的时候就可以用到 return new SimpleAuthenticationInfo(user, user.getPassword(), ""); } // 当访问的资源需要权限时,执行该方法。Spring Security的UserDetails包含认证信息和权限信息,所以只需要一个loadUserByUsername即可。而shiro的认证信息和权限信息是分开的,所以需要俩个方法 // 返回用户拥有的权限信息内容 @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { System.out.println("执行了=>授权doGetAuthorizationInfo"); SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo(); // 获取当前登录用户的信息(在认证的时候保存的) User user = (User) SecurityUtils.getSubject().getPrincipal(); // 告诉shiro当前用户有哪些权限 List permissions = Arrays.stream(user.getPerms().split(",")).collect(Collectors.toList()); authorizationInfo.addStringPermissions(permissions); return authorizationInfo; } }
@Configuration public class ShiroConfig { // 创建realm对象 @Bean public UserRealm userRealm() { return new UserRealm(); } // 定义DefaultSecurityManager,并管理Realm @Bean public DefaultWebSecurityManager defaultWebSecurityManager() { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); securityManager.setRealm(userRealm()); return securityManager; } // 定义资源访问规则 @Bean public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultSecurityManager defaultSecurityManager) { ShiroFilterFactoryBean filterFactoryBean = new ShiroFilterFactoryBean(); filterFactoryBean.setSecurityManager(defaultSecurityManager); // 添加shiro内置的过滤器,配置资源访问规则 MapfilterMap = new linkedHashMap<>(); // 告诉shiro资源的权限规则。注意顺序不要反了 filterMap.put("/r/r1","perms[user:add]"); // /r/r1资源需要拥有user:add权限 filterMap.put("/r/r2","perms[user:update]"); // /r/r2资源需要拥有user:update权限 filterMap.put("/r/**", "authc"); //所有/r开头的资源都需要认证后才能访问 filterFactoryBean.setFilterChainDefinitionMap(filterMap); // 自定义未授权页面 filterFactoryBean.setUnauthorizedUrl("/unauthorized"); // 登录页面 filterFactoryBean.setLoginUrl("/toLogin"); return filterFactoryBean; } }
需要注意的它的匹配规则是从上到下的,是否允许访问,取决于第一条匹配的规则是否通过。如果将/r/**的规则写在上面,意味着只要登录了,就可以访问所有的资源。而我们配置的权限规则将会失效。所以我们一般将粒度大的规则写在后面。
4、定义登录接口、推出登录接口@Controller public class LoginController { @RequestMapping("login") public String login(String username, String password, Model model) { Subject subject = SecurityUtils.getSubject(); UsernamePasswordToken token = new UsernamePasswordToken(username, password); try { subject.login(token); // 进行登录 *** 作 return "loginSuccess"; } catch (UnknownAccountException uae) { model.addAttribute("msg", "用户名不存在"); } catch (IncorrectCredentialsException ice) { model.addAttribute("msg", "密码错误"); } return "login"; } @RequestMapping("logout") public String logout() { Subject subject = SecurityUtils.getSubject(); if(subject.isAuthenticated()) { subject.logout(); } System.out.println("退出登录成功"); return "login"; } // 跳转到登录页面 @GetMapping("/toLogin") public String toLogin() { return "login"; } }
这里简单介绍一下Shiro的身份认证流程:
- 首先调用 Subject.login(token) 进行登录,其会自动委托给 Security Manager
- SecurityManager 负责真正的身份验证逻辑;它会委托给 Authenticator 进行身份验证;
- Authenticator 才是真正的身份验证者,Shiro API 中核心的身份认证入口点,此处可以自定义插入自己的实现;
- Authenticator 可能会委托给相应的 AuthenticationStrategy 进行多 Realm 身份验证,默认 ModularRealmAuthenticator 会调用 AuthenticationStrategy 进行多 Realm 身份验证;
- Authenticator 会把相应的 token 传入 Realm,从 Realm 获取身份验证信息,如果没有返回 / 抛出异常表示身份验证成功了。此处可以配置多个 Realm,将按照相应的顺序及策略进行访问。
这段建议多看几遍,后面如果看源码,可以根据这个流程来看
至此Shiro的授权、认证流程已经完成了。剩下的就是一下测试用的代码了
5、定义资源文件@RestController public class ResourceController { // 需要用户有user:add权限 @GetMapping("/r/r1") public String r1() { return "资源r1"; } // 需要用户有user:update权限 @GetMapping("/r/r2") public String r2() { return "资源r2"; } // 只需登录即可访问。 @GetMapping("/r/r3") public String r3() { return "资源r3"; } // 允许匿名访问的资源 @GetMapping("/anony") public String anony(){ return "匿名资源"; } }6. 在resource/templates下创建login.html页面
启动项目,验证下面几个测试用例
- 登录小王账号,所有的资源均可访问
- 登录小明账号,/r/r1资源没有权限访问,其他均可
- 未登录,只有匿名资源可以访问
简单总结一下几个关键的点:
- 配置Realm,Shiro需要借助Reaml获取用户的认证、授权等信息
- 配置安全管理器,管理Realm
- 通过ShiroFilterFactoryBean定义资源权限访问规则
- 通过Subject的login方法进行登录。logout方法进行推出登录
这只是一篇入门的文章,通过该文章,你可以快速的了解如何使用Shiro。此外为了避免篇幅部分非关键代码并没有贴出来,代码我已经放在码云仓库了。同时建议,如果你可以找一些使用了shiro的开源项目,看看在项目中如何使用shiro。
如果你想对shiro的机制更进一步的了解,你或许还需要去研究shiro的源码
个人碎叨叨
在搭建了Shiro和Spring Security的入门案例,个人觉得啊,Spring Security真的比Shiro好用多了。Spring Security帮我们做了大部分的东西,我们只需要少量的配置即可快速使用。
而且我感觉Spring Security API的设计也比Shiro好。比如资源访问权限规则的,Shiro是在Map中配置的,Spring Security的API就挺好的,还有密码加密,感觉Shiro的密码加密API设计不太好,Spring Security密码加密的API使用起来就很方便(这也是为什么Shiro的密码加密我要另外写一篇的原因,写在一起的话,代码的复杂度就提升很多了,怕影响理解)
参考资料
https://www.w3cschool.cn/shiro/
其他链接
- Spring Security整合Spring Boot快速入门
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)