zoukankan      html  css  js  c++  java
  • shiro

          最近写了一个关于shiro集成Spring Boot的demo,感受了一下shiro的技能。

    功能介绍:1,用户进行登录,此时地址栏中只能访问登录页面,其余都会被拦截。

                  2,登录时根据用户登录状态给出 登陆成功;当前用户不存在;密码错误 三种状态。

                  3,登录成功后,页面直接显示当前用户所拥有的权限功能(也可以显示全部功能,根据当前用户的角色权限进行操作,未授权的给出无法访问的提示信息),未授权的功能不显示。

    代码介绍:1,此demo的pom.xml,里面大部分有注解,可以根据需要拿.

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
    
        <groupId>me.tianle</groupId>
        <artifactId>login</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <packaging>jar</packaging>
    
        <name>login</name>
        <description>Demo project for Spring Boot</description>
    
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.0.1.RELEASE</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
    
        <properties>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
            <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
            <java.version>1.8</java.version>
        </properties>
    
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-thymeleaf</artifactId>
            </dependency>
    
            <!-- web支持,SpringMVC, Servlet支持等 -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <!-- 导入thymeleaf依赖 -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-thymeleaf</artifactId>
            </dependency>
    
            <!--shiro依赖-->
            <dependency>
                <groupId>org.apache.shiro</groupId>
                <artifactId>shiro-spring</artifactId>
                <version>1.4.0</version>
            </dependency>
    
            <dependency>
                <groupId>org.mybatis.spring.boot</groupId>
                <artifactId>mybatis-spring-boot-starter</artifactId>
                <version>1.1.1</version>
            </dependency>
    
            <!-- mysql -->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
            </dependency>
    
            <!-- 导入mybatis相关的依赖 -->
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>druid</artifactId>
                <version>1.0.9</version>
            </dependency>
    
            <!-- thymel对shiro的扩展坐标 -->
            <dependency>
                <groupId>com.github.theborakompanioni</groupId>
                <artifactId>thymeleaf-extras-shiro</artifactId>
                <version>2.0.0</version>
            </dependency>
    
        </dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
    
    
    </project>

       2,shiro配置类,shiro有三个核心组件:Subject, SecurityManager 和 Realms.

    package me.tianle.login.shiro;
    
    import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
    import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
    import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.apache.shiro.mgt.SecurityManager;
    
    import java.util.LinkedHashMap;
    import java.util.Map;
    
    @Configuration
    public class ShiroConfig {
        @Bean
        public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
            ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
            // 必须设置 SecurityManager
            shiroFilterFactoryBean.setSecurityManager(securityManager);
            // setLoginUrl 如果不设置值,默认会自动寻找Web工程根目录下的"/login.jsp"页面 或 "/login" 映射
            shiroFilterFactoryBean.setLoginUrl("/ ");
            // 设置无权限时跳转的 url;
            shiroFilterFactoryBean.setUnauthorizedUrl("/notRole");
    
            /**
             * Shiro内置过滤器,可以实现权限相关的拦截器
             *    常用的过滤器:
             *       anon: 无需认证(登录)可以访问
             *       authc: 必须认证才可以访问
             *       user: 如果使用rememberMe的功能可以直接访问
             *       perms: 该资源必须得到资源权限才可以访问
             *       role: 该资源必须得到角色权限才可以访问
             */
    
            // 设置拦截器
            Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
            //开放登陆接口
            filterChainDefinitionMap.put("/login", "anon");
            filterChainDefinitionMap.put("/", "anon");
    
            //设置未授权提示页面
            shiroFilterFactoryBean.setUnauthorizedUrl("/noAuth");
            //授权过滤器
            //注意:当前授权拦截后,shiro会自动跳转到未授权页面
            filterChainDefinitionMap.put("/add", "perms[添加]");
            filterChainDefinitionMap.put("/update", "perms[修改]");
            filterChainDefinitionMap.put("/del", "perms[删除]");
            filterChainDefinitionMap.put("/detail", "perms[查看]");
    
            //其余接口一律拦截
            //主要这行代码必须放在所有权限设置的最后,不然会导致所有 url 都被拦截
            filterChainDefinitionMap.put("/*", "authc");
    
            shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
            System.out.println("Shiro拦截器工厂类注入成功");
            return shiroFilterFactoryBean;
        }
    
        /**
         * 注入 securityManager
         */
        @Bean
        public SecurityManager securityManager() {
            DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
            // 设置realm.
            securityManager.setRealm(getRealm());
            return securityManager;
        }
    
        /**
         * 自定义身份认证 realm;
         * <p>
         * 必须写这个类,并加上 @Bean 注解,目的是注入 UserRealm,
         * 否则会影响 UserRealm类 中其他类的依赖注入
         */
        @Bean
        public UserRealm getRealm() {
            return new UserRealm();
        }
    
        /**
         * 配置ShiroDialect,用于thymeleaf和shiro标签配合使用
         */
        @Bean
        public ShiroDialect getShiroDialect(){
            return new ShiroDialect();
        }
    }

     /** * 配置ShiroDialect,用于thymeleaf和shiro标签配合使用 */   里面的方法是用来连接spring boot的模板thymeleaf和shiro的.

        3,Realm类,跟shiro配置类配合使用,里面实现了两个方法,用来执行授权逻辑方法和执行认证逻辑方法.授权用于登录后用户的角色授权,认证用于登录时的几种情况分别提示。

    package me.tianle.login.shiro;
    
    import me.tianle.login.model.Permission;
    import me.tianle.login.model.User;
    import me.tianle.login.service.UserService;
    import org.apache.shiro.SecurityUtils;
    import org.apache.shiro.authc.UsernamePasswordToken;
    import org.apache.shiro.authc.SimpleAuthenticationInfo;
    import org.apache.shiro.authc.AuthenticationToken;
    import org.apache.shiro.authc.AuthenticationInfo;
    import org.apache.shiro.authc.AuthenticationException;
    import org.apache.shiro.authz.AuthorizationInfo;
    import org.apache.shiro.authz.SimpleAuthorizationInfo;
    import org.apache.shiro.realm.AuthorizingRealm;
    import org.apache.shiro.subject.PrincipalCollection;
    import org.apache.shiro.subject.Subject;
    import org.springframework.beans.factory.annotation.Autowired;
    
    import java.util.List;
    
    public class UserRealm extends AuthorizingRealm {
    
        @Autowired
        private UserService userService;
    
        /**
         * 执行授权逻辑
         */
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
            System.out.println("授权");
    
            //给资源进行授权
            SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
    
            //添加资源的授权字符串
            //info.addStringPermission("user:add");
    
            //到数据库查询当前登录用户的授权字符串
            //获取当前登录用户
            Subject subject = SecurityUtils.getSubject();
            User user = (User)subject.getPrincipal();
            List<Permission> list = userService.findPermission(user.getUid());
            System.out.println(list);
            for (Permission per:list){
                info.addStringPermission(per.getName());
                System.out.println("mmm: "+ per.getName());
            }
            return info;
        }
    
        /**
         * 执行认证逻辑
         */
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
            System.out.println("执行认证");
            UsernamePasswordToken token = (UsernamePasswordToken)authenticationToken;
            User user = userService.findByUsername(token.getUsername());
            if(user==null){
                //用户名不存在
                return null;
            }
            return new SimpleAuthenticationInfo(user,user.getPassword(),"");
        }
    
    }

              4,controller层 主要体现在登录过程中,login()方法中抛出的两个异常是shiro这些工具类中封装好的,可以直接拿来使用,包括里面的UsernamePasswordToken  用来封装用户的用户名称和用户密码。

    package me.tianle.login.controller;
    
    import org.apache.shiro.SecurityUtils;
    import org.apache.shiro.authc.IncorrectCredentialsException;
    import org.apache.shiro.authc.UnknownAccountException;
    import org.apache.shiro.authc.UsernamePasswordToken;
    import org.apache.shiro.subject.Subject;
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.servlet.ModelAndView;
    
    @Controller
    public class LoginController {
    
    
        @RequestMapping("")
        public ModelAndView index() {
            return new ModelAndView("index");
        }
    
        @RequestMapping("/add")
        public String add(){
            return "/per/addUser";
        }
    
        @RequestMapping("/detail")
        public String detail(){
            return "/per/detail";
        }
    
        @RequestMapping("/del")
        public String del(){
            return "/per/delUser";
        }
    
        @RequestMapping("/update")
        public String update(){
            return "/per/updateUser";
        }
    
        //未授权页面跳转
        @RequestMapping("/noAuth")
        public String noAuth(){
            return "noAuth";
        }
    
        //登陆成功页面
        @RequestMapping("/success")
        public String success(){
            return "success";
        }
    
        //退出当前用户
        @RequestMapping("/logout")
        public String logout(){
            Subject subject = SecurityUtils.getSubject();
            subject.logout();
            return "index";
        }
    
        /**
         * 登录逻辑处理
         */
        @RequestMapping("/login")
        public String login(String username, String password, Model model){
            System.out.println("name="+username);
            /**
             * 使用Shiro编写认证操作
             */
            //1.获取Subject
            Subject subject = SecurityUtils.getSubject();
    
            //2.封装用户数据
            UsernamePasswordToken token = new UsernamePasswordToken(username,password);
    
            //3.执行登录方法
            try {
                subject.login(token);
    
                //登录成功
                //跳转到test.html
                return "redirect:/success";
            } catch (UnknownAccountException e) {
                //e.printStackTrace();
                //登录失败:用户名不存在
                model.addAttribute("msg", "用户名不存在!");
                return "index";
            }catch (IncorrectCredentialsException e) {
                //e.printStackTrace();
                //登录失败:密码错误
                model.addAttribute("msg", "密码错误!");
                return "index";
            }
        }
    
    }

             5,我的登录页面用的是index.html,和登录成功之后的一个success.html页面

    <!DOCTYPE html>
    <html lang="zh" xmlns:th="http://www.w3.org/1999/xhtml">
    <head>
        <meta charset="UTF-8">
        <title>用户登录</title>
        <link rel='stylesheet' href='http://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css'>
        <script src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.8.0.js"></script>
    </head>
    <body class="form-bg">
    <div class="container my-form">
        <div class="row">
            <div class="col-md-offset-3 col-md-6">
                <!--th:object="${user}"-->
                <form class="form-horizontal" th:action="@{/login}" method="post">
                    <span class="heading">用户登录</span>
                    <div class="form-group">
                        <input type="text" name="username" class="form-control" id="username" placeholder="用户名">
                        <i class="fa fa-user"></i>
                    </div>
                    <div class="form-group help">
                        <input type="password" name="password" class="form-control" id="password" placeholder="密 码">
                        <i class="fa fa-lock"></i>
                    </div>
                    <div class="form-group">
                        <button type="submit" class="btn btn-default">登录</button>
                    </div>
                    <div>
                        <h3 th:text="${msg}" style="color: red"></h3>
                    </div>
                </form>
            </div>
        </div>
    </div>
    
    </body>
    <style>
        .my-form {
            margin-top: 100px;
        }
    
        .form-bg {
            background: #bbd5ef;
        }
    
        .form-horizontal {
            background: #fff;
            padding-bottom: 40px;
            border-radius: 15px;
            text-align: center;
        }
    
        .form-horizontal .heading {
            display: block;
            font-size: 25px;
            font-weight: 700;
            padding: 35px 0;
            border-bottom: 1px solid #f0f0f0;
            margin-bottom: 30px;
        }
    
        .form-horizontal .form-group {
            padding: 0 40px;
            margin: 0 0 25px 0;
            position: relative;
        }
    
        .form-horizontal .form-control {
            background: #f0f0f0;
            border: none;
            border-radius: 20px;
            box-shadow: none;
            padding: 0 20px 0 45px;
            height: 40px;
            transition: all 0.3s ease 0s;
        }
    
        .form-horizontal .form-control:focus {
            background: #e0e0e0;
            box-shadow: none;
            outline: 0 none;
        }
    
        .form-horizontal .form-group i {
            position: absolute;
            top: 12px;
            left: 60px;
            font-size: 17px;
            color: #c8c8c8;
            transition: all 0.5s ease 0s;
        }
    
        .form-horizontal .form-control:focus + i {
            color: #00b4ef;
        }
    
        .form-horizontal .fa-question-circle {
            display: inline-block;
            position: absolute;
            top: 12px;
            right: 60px;
            font-size: 20px;
            color: #808080;
            transition: all 0.5s ease 0s;
        }
    
        .form-horizontal .fa-question-circle:hover {
            color: #000;
        }
    
        .form-horizontal .main-checkbox {
            float: left;
             20px;
            height: 20px;
            background: #11a3fc;
            border-radius: 50%;
            position: relative;
            margin: 5px 0 0 5px;
            border: 1px solid #11a3fc;
        }
    
        .form-horizontal .main-checkbox label {
             20px;
            height: 20px;
            position: absolute;
            top: 0;
            left: 0;
            cursor: pointer;
        }
    
        .form-horizontal .main-checkbox label:after {
            content: "";
             10px;
            height: 5px;
            position: absolute;
            top: 5px;
            left: 4px;
            border: 3px solid #fff;
            border-top: none;
            border-right: none;
            background: transparent;
            opacity: 0;
            -webkit-transform: rotate(-45deg);
            transform: rotate(-45deg);
        }
    
        .form-horizontal .main-checkbox input[type=checkbox] {
            visibility: hidden;
        }
    
        .form-horizontal .main-checkbox input[type=checkbox]:checked + label:after {
            opacity: 1;
        }
    
        .form-horizontal .text {
            float: left;
            margin-left: 7px;
            line-height: 20px;
            padding-top: 5px;
            text-transform: capitalize;
        }
    
        .form-horizontal .btn {
            float: right;
            font-size: 14px;
            color: #fff;
            background: #00b4ef;
            border-radius: 30px;
            padding: 10px 25px;
            border: none;
            text-transform: capitalize;
            transition: all 0.5s ease 0s;
        }
    
        @media only screen and (max- 479px) {
            .form-horizontal .form-group {
                padding: 0 25px;
            }
    
            .form-horizontal .form-group i {
                left: 45px;
            }
    
            .form-horizontal .btn {
                padding: 10px 20px;
            }
        }
    </style>
    </html>
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <h3>登陆成功</h3>
    
    <hr/>
    <div shiro:hasPermission="添加">
        添加功能: <a href="add">用户添加</a><br/>
    </div>
    
    <div shiro:hasPermission="修改">
        更新功能: <a href="update">用户更新</a><br/>
    </div>
    
    <div shiro:hasPermission="删除">
        删除功能: <a href="del">用户删除</a><br/>
    </div>
    
    <div shiro:hasPermission="查看">
        查看详情: <a href="detail">用户详情</a><br/>
    </div>
    <h4><a href="logout">退出</a></h4>
    </body>
    </html>

          6,数据库里面有5张表,用户表,角色表,权限表,还有两张他们的关系表.

                     以上就是此demo的重要组成部分,如果觉得不够清晰的话,可以访问我的仓库地址  https://gitee.com/whatever110/SpringBoot-Html.git  或者去看看shiro的视频讲解,再见喽!

  • 相关阅读:
    基于visual c++之windows核心编程代码分析(52)使用WMI 获取进程启动参数
    基于visual c++之windows核心编程代码分析(56)使用winsock搜索蓝牙设备
    这个星期上网找工作,周六准备就去上海了.
    Delphi 中的 XMLDocument 类详解(21) 暂停
    数组与子界
    Delphi 中的 XMLDocument 类详解(17) 上一个节点、下一个节点、父节点
    Delphi 中的 XMLDocument 类详解(19) NodeValue 与 NodeName 的读写区别
    Delphi 中的 XMLDocument 类详解(18) 更好地显示 xml 的测试结果 FormatXMLData 及其他
    Delphi 中的 XMLDocument 类详解(16) 节点列表中的第一个与最后一个节点
    Delphi 中的 XMLDocument 类详解(14) 遍历 XML 文件
  • 原文地址:https://www.cnblogs.com/BKhp/p/11232956.html
Copyright © 2011-2022 走看看