Shiro整合Spring Boot入门

Shiro整合Spring Boot入门,第1张

Shiro整合Spring Boot入门

这是一篇Shiro入门的博文,将整合Spring Boot快速入门Shiro。实现基本的认证、授权、密码加密功能
认证:也就是我们平时所说的登录,但认证不局限于账号密码登录,扫码、人脸识别、指纹等都可以算是认证
授权:不同的人拥有不同的权限,比如在后台管理系统中,管理员和普通用户看到的菜单是不一样的、有些资源普通用户只有读权限没有写权限等

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,即安全数据源。
Shiro整合Spring Boot快速入门 1、导入依赖

    org.springframework.boot
    spring-boot-starter-web


    org.apache.shiro
    shiro-spring-boot-starter
    1.8.0



    org.springframework.boot
    spring-boot-starter-thymeleaf

2、自定义一个UserRealm。还记得Realm是干嘛的吗?
// 作用类比Spring Security中的UserDetailsService
public class UserRealm extends AuthorizingRealm {

    // 模拟数据库中的数据
    private Map 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;
    }
}
3、配置安全管理器和资源访问规则
@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内置的过滤器,配置资源访问规则
        
        Map filterMap = 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的身份认证流程:

  1. 首先调用 Subject.login(token) 进行登录,其会自动委托给 Security Manager
  2. SecurityManager 负责真正的身份验证逻辑;它会委托给 Authenticator 进行身份验证;
  3. Authenticator 才是真正的身份验证者,Shiro API 中核心的身份认证入口点,此处可以自定义插入自己的实现;
  4. Authenticator 可能会委托给相应的 AuthenticationStrategy 进行多 Realm 身份验证,默认 ModularRealmAuthenticator 会调用 AuthenticationStrategy 进行多 Realm 身份验证;
  5. 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页面
 

启动项目,验证下面几个测试用例

  1. 登录小王账号,所有的资源均可访问
  2. 登录小明账号,/r/r1资源没有权限访问,其他均可
  3. 未登录,只有匿名资源可以访问

简单总结一下几个关键的点:

  1. 配置Realm,Shiro需要借助Reaml获取用户的认证、授权等信息
  2. 配置安全管理器,管理Realm
  3. 通过ShiroFilterFactoryBean定义资源权限访问规则
  4. 通过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快速入门

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/4660445.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-11-06
下一篇 2022-11-06

发表评论

登录后才能评论

评论列表(0条)

保存