SpringBoot+Shiro实现登录授权认证

一、Shiro介绍

Apache Shiro是一个强大且易用的Java安全框架,执行身份验证、授权、密码和会话管理。使用Shiro的易于理解的API,您可以快速、轻松地获得任何应用程序,从最小的移动应用程序到最大的网络和企业应用程序。
他有三大核心组件

  • Subject
    即“当前操作用户”。但是,在 Shiro 中,Subject 这一概念并不仅仅指人,也可以是第三方进程、后台帐户(Daemon Account)或其他类似事物。它仅仅意味着“当前跟软件交互的东西”。Subject 代表了当前用户的安全操作,SecurityManager 则管理所有用户的安全操作。
  • SecurityManager
    它是Shiro 框架的核心,典型的 Facade 模式,Shiro 通过 SecurityManager 来管理内部组件实例,并通过它来提供安全管理的各种服务。
  • Realm
    Realm 充当了 Shiro 与应用安全数据间的“桥梁”或者“连接器”。也就是说,当对用户执行认证(登录)和授权(访问控制)验证时,Shiro 会从应用配置的 Realm 中查找用户及其权限信息。从这个意义上讲,Realm 实质上是一个安全相关的 DAO:它封装了数据源的连接细节,并在需要时将相关数据提供给 Shiro。当配置 Shiro 时,你必须至少指定一个 Realm,用于认证和(或)授权。配置多个 Realm 是可以的,但是至少需要一个。Shiro 内置了可以连接大量安全数据源(又名目录)的 Realm,如 LDAP、关系数据库(JDBC)、类似 INI 的文本配置资源以及属性文件等。如果缺省的 Realm 不能满足需求,你还可以插入代表自定义数据源的自己的 Realm 实现。

二、SpringBoot 整合 Shiro

这里就以SpringBoot开发的一个博客项目中的登录模块为例,来展示与Shiro的整合,从而实现登录的认证。

  • 1、引入Shiro相关依赖

    <!--shiro核心-->
    <dependency>
        <groupId>org.apache.shiro</groupId>
        <artifactId>shiro-core</artifactId>
        <version>1.3.2</version>
    </dependency>
    <!--shiro整合spring的包-->
    <dependency>
        <groupId>org.apache.shiro</groupId>
        <artifactId>shiro-spring</artifactId>
        <version>1.9.0</version>
    </dependency>
    <!--shiro整合thymeleaf的包-->
    <dependency>
        <groupId>com.github.theborakompanioni</groupId>
        <artifactId>thymeleaf-extras-shiro</artifactId>
        <version>2.0.0</version>
    </dependency>
    

    我的SpringBoot 版本是 2.6.4,Shiro版本自行选择。

  • 2、login.html

    <form class="ui large form" th:action="@{/admin/login}" method="post" >
        <div class="ui segment">
            <div class="field">
                <div class="ui left icon input">
                    <i class="user icon"></i>
                    <input type="text" name="username" placeholder="用户名">
                </div>
            </div>
            <div class="field">
                <div class="ui left icon input">
                    <i class="lock icon"></i>
                    <input type="password" name="password" placeholder="密码">
                </div>
            </div>
            <button class="ui fluid large teal submit button">
                登   录
            </button>
        </div>
        <div class="ui error mini message"></div>
        <div class="ui mini negative message" th:if="${message!=null}" th:text="${message}"></div>
    </form>
    

    登录页面表单部分,管理员输入用户名和密码后登录提交到 /admin/login 中去处理。

  • 3、LoginController

    @Controller
    @RequestMapping("/admin")
    public class LoginController {
    
        @Autowired
        UserService userService;
    
        //去登录页面
        @RequestMapping("/toLogin")
        public String loginPage(){
            return "admin/login";
        }
    
        //登录
        @RequestMapping("/login")
        public String login(@RequestParam("username") String username,
                            @RequestParam("password") String password,
                            Model model){
            System.out.println("登录...............................\n\n");
            System.out.println("登录用户名:" + username);
            System.out.println("登录密码:" + password);
            //获取当前用户
            Subject subject = SecurityUtils.getSubject();
            //封装用户的登录数据
            UsernamePasswordToken token = new UsernamePasswordToken(username, MD5Utils.code(password));
            try{
                subject.login(token);//执行登录的方法,如果没有异常就说明ok
                return "admin/index";
            }catch (UnknownAccountException e){     //用户名不存在
                model.addAttribute("message","用户名错误!");
                return "admin/login";
            }catch (IncorrectCredentialsException e){//密码错误
                model.addAttribute("message","密码错误!");
                return "admin/login";
            }
        }
    
        //无认证
        @RequestMapping("/toNoauth")
        public String Unauthorized(){
            return "admin/noauth";
        }
    
        //logout登出功能 shiro已经实现!
    }
    

    将传过来的用户名和密码封装成登录数据 UsernamePasswordToken ,并使用shiro的subject组件自带的login方法,进行登录。

    编写自定义的Realm,在Realm中主要工作就是授权和认证,连接真实数据库,根据刚封装token中的username查询用户是否存在,如果不存在,则会抛出UnknownAccountException 异常,如果存在,则继续将封装token中的密码与真实数据库中的密码比对,当然,密码校验shiro已经帮我们做了,不用我们自己写,如果比对不成功,则抛出IncorrectCredentialsException 异常,如果用户存在且密码比对成功,则 return “admin/index”; 成功进入首页。

    这边还定义了当没有认证直接访问管理员首页及其它页面时,跳转的controller /toNoauth,logout登出功能shiro已经帮我们实现,不需要自己写。

  • 4、自定义Realm UserRealm

    //自定义的UserRealm
    public class UserRealm extends AuthorizingRealm {
    
        @Autowired
        UserService userService;
    
        //授权
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
            System.out.println("执行了 -》 授权 doGetAuthorizationInfo");
            return null;
        }
    
        //认证
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
            System.out.println("执行了 -》 认证 doGetAuthenticationInfo");
    
            UsernamePasswordToken userToken = (UsernamePasswordToken)token;
            System.out.println("userToken -> username" + userToken.getUsername());
            System.out.println("userToken -> password" + userToken.getPassword());
            //获取数据库中真实用户
            User user = userService.queryUserByName(userToken.getUsername());
            System.out.println("user ->" + user);
            if(user == null){
                return null;
            }
            //把用户存入session
            Subject subject = SecurityUtils.getSubject();
            subject.getSession().setAttribute("user",user);
            //密码认证,shiro来做 不让你来做
            return new SimpleAuthenticationInfo(user,user.getPassword(),"");
        }
    }
    

    好了,到这一步,管理员登录认证已经完成,接下来也是最后一步,我们需要编写ShiroConfig配置类来决定哪些请求可以放行,哪些请求需要拦截认证,未认证/授权跳转的页面,登录成功跳转的页面等。

  • 5、ShiroConfig

    @Configuration
    public class ShiroConfig {
    
        //ShiroFilterFactoryBean :第三步
        @Bean
        public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager")
                                                                            DefaultWebSecurityManager
                                                                            securityManager) {
            System.out.println("ShiroConfiguration.shirFilter()");
            ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
            //必须设置 SecurityManager 如果不设置就无法完成认证和授权
            bean.setSecurityManager(securityManager);
            //添加Shiro的内置过滤器
            /*
             * anno:无需认证就可以访问
             * authc:必须认证才能访问
             * user:必须拥有 记住我 功能才能用
             * perms:拥有对某个资源的权限才能访问
             * role:拥有某个角色权限 才能访问
             * */
            Map<String,String> filterMap = new LinkedHashMap<>();
            // 配置退出 过滤器,其中的具体的退出代码Shiro已经替我们实现了
            // logout shiro定义好的过滤器名字 /logout访问路径
            // 浏览器访问的地址栏路径中以/logout结尾的路径 走logout过滤器
            // logout会清除session 退出登录
            filterMap.put("/admin/logout", "logout");
            //所有的img js css文件走 anon过滤器 此过滤器代表放过拦截 不需要权限也能访问
            filterMap.put("/css/**", "anon");
            filterMap.put("/images/**", "anon");
            filterMap.put("/js/**", "anon");
            //放过登录页面拦截
            filterMap.put("/admin/toLogin", "anon");
            filterMap.put("/admin/login", "anon");
            // **代表所有路径 除以上路径外的管理员页面都拦截
            filterMap.put("/admin/**", "authc");
            //设置未认证的请求
            bean.setLoginUrl("/admin/toNoauth");
            bean.setFilterChainDefinitionMap(filterMap);
            //登录成功跳转的页面
            bean.setSuccessUrl("admin/index");
            //设置未授权的请求
            bean.setUnauthorizedUrl("/admin/toNoauth");
    
            return bean;
        }
    
        //DefaultWebSecurityManager :第二步
        @Bean(name = "securityManager")
        public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm) {
            DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
            //关联UserRealm
            securityManager.setRealm(userRealm);
            return securityManager;
        }
    
        //创建realm对象,需要自定义对象 :第一步
        @Bean(name = "userRealm")
        public UserRealm userRealm(){
            return new UserRealm();
        }
    
        //整合ShiroDialect,用于整合shiro thymeleaf
        @Bean
        public ShiroDialect getShiroDialect() {
            return new ShiroDialect();
        }
    }
    
  • 6、测试
    (1) 登录页面
    登录页面

    (2)当没有登录认证,直接访问管理员首页
    未认证

    (3)当用户不存在
    用户不存在

    (4)当密码错误
    密码错误

    (5)当用户名和密码正确
    登录成功

    (6)登录成功后访问管理员首页

    (7)登出
    (8)登出后再访问管理员首页在这里插入图片描述

  • 0
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

歪歪坨

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值