IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> SpringBoot+Shiro实现登录授权认证 -> 正文阅读

[Java知识库]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)登出后再访问管理员首页在这里插入图片描述

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-05-12 16:20:27  更:2022-05-12 16:22:48 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 22:45:13-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码