zoukankan      html  css  js  c++  java
  • shiro(三)——springboot整合shiro

    该整合项目完全参照 狂神说java 的《springboot整合shiro框架》教学视频完成,如有不懂的地方可以查看该教学视频。

    目录:

    1.该整合项目所需的依赖

        <dependencies>
    
            <!--
            subject 用户
            securityManager 管理所有用户
            realm  连接数据
            -->
    
            <!--连接数据库的依赖-->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>8.0.15</version>
            </dependency>
            <dependency>
                <groupId>log4j</groupId>
                <artifactId>log4j</artifactId>
                <version>1.2.17</version>
            </dependency>
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>druid</artifactId>
                <version>1.1.13</version>
            </dependency>
    
            <!--引入mybatis,这是mybatis官方提供的适配springboot的,而不是springboot自己的-->
            <dependency>
                <groupId>org.mybatis.spring.boot</groupId>
                <artifactId>mybatis-spring-boot-starter</artifactId>
                <version>2.1.2</version>
            </dependency>
    
            <!--不想书写setter、getter方法,导入此依赖-->
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>1.16.22</version>
            </dependency>
    
            <!--shiro整合spring的包-->
            <dependency>
                <groupId>org.apache.shiro</groupId>
                <artifactId>shiro-spring</artifactId>
                <version>1.4.2</version>
            </dependency>
    
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
    
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
                <exclusions>
                    <exclusion>
                        <groupId>org.junit.vintage</groupId>
                        <artifactId>junit-vintage-engine</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
    
            <!--导入thymeleaf依赖-->
            <dependency>
                <groupId>org.thymeleaf</groupId>
                <artifactId>thymeleaf-spring5</artifactId>
                <version>3.0.11.RELEASE</version>
            </dependency>
            <dependency>
                <groupId>org.thymeleaf.extras</groupId>
                <artifactId>thymeleaf-extras-java8time</artifactId>
                <version>3.0.4.RELEASE</version>
            </dependency>
    
            <!--shiro-thymeleaf整合-->
            <dependency>
                <groupId>com.github.theborakompanioni</groupId>
                <artifactId>thymeleaf-extras-shiro</artifactId>
                <version>2.0.0</version>
            </dependency>
        </dependencies>
        
        <repositories>
            <repository>
                <id>aliyun-repos</id>
                <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
                <snapshots>
                    <enabled>false</enabled>
                </snapshots>
            </repository>
        </repositories>
        <pluginRepositories>
            <pluginRepository>
                <id>aliyun-plugin</id>
                <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
                <snapshots>
                    <enabled>false</enabled>
                </snapshots>
            </pluginRepository>
        </pluginRepositories>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
        
    </project>

     2. application.yml设置连接数据库的相关配置

    spring:
      datasource:
        username: root
        password: 123456
        #?serverTimezone=UTC解决时区的报错
        url: jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
        driver-class-name: com.mysql.cj.jdbc.Driver
        type: com.alibaba.druid.pool.DruidDataSource
    
        #Spring Boot 默认是不注入这些属性值的,需要自己绑定
        #druid 数据源专有配置
        initialSize: 5
        minIdle: 5
        maxActive: 20
        maxWait: 60000
        timeBetweenEvictionRunsMillis: 60000
        minEvictableIdleTimeMillis: 300000
        validationQuery: SELECT 1 FROM DUAL
        testWhileIdle: true
        testOnBorrow: false
        testOnReturn: false
        poolPreparedStatements: true
    
        #配置监控统计拦截的filters,stat:监控统计、log4j:日志记录、wall:防御sql注入
        #如果允许时报错  java.lang.ClassNotFoundException: org.apache.log4j.Priority
        #则导入 log4j 依赖即可,Maven 地址:https://mvnrepository.com/artifact/log4j/log4j
        filters: stat,wall,log4j
        maxPoolPreparedStatementPerConnectionSize: 20
        useGlobalDataSourceStat: true
        connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500

     3.index.html

    <!DOCTYPE html>
    <html lang="en" xmlns:th="http://www.w3.org/1999/xhtml?"
          xmlns:shiro="http://www.thymeleaf.org/thymeleaf-extras-shiro" >
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
        <h1>首页</h1>
    
        <div th:if="${session.loginUser==null}">
            <a th:href="@{/toLogin}">登录</a>
        </div>
    
        <p th:text="${msg}"></p>
    
        <hr>
        <div shiro:hasPermission="user:add">
            <a th:href="@{/user/add}">add</a>
        </div>
        <div shiro:hasPermission="user:update">
            <a th:href="@{/user/update}">update</a>
        </div>
    
        <a th:href="@{/logout}">注销</a>
    
    </body>
    </html>

    4.add.html和update.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <h1>add</h1>
    </body>
    </html>
    
    -------------------------------------------------
    
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <h1>update</h1>
    </body>
    </html>

    5.login.html

    <!DOCTYPE html>
    <html lang="en" xmlns:th="http://www.w3.org/1999/xhtml?">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <h1>登录</h1>
    <hr>
    <p th:text="${msg}" style="color:red;"></p>
    <form th:action="@{/login}">
        用户名:<input type="text" name="username"><br>
        密码:<input type="password" name="password">
        <br>
        <input type="submit" name="提交">
    </form>
    </body>
    </html>

    6.MyController.java

    @Controller
    public class MyController {
    
        @RequestMapping({"/","/index"})
        public String toIndex(Model model){
            model.addAttribute("msg","hello,shiro!");
            return "index";
        }
    
        @RequestMapping("/user/add")
        public String add(){
            return "user/add";
        }
    
        @RequestMapping("/user/update")
        public String update(){
            return "user/update";
        }
    
        @RequestMapping("/toLogin")
        public String toLogin(){
            return "login";
        }
    
        @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); //执行登录的方法,如果没有异常就说明ok了
                return "index";
            }catch (UnknownAccountException e){ //用户名不存在
                model.addAttribute("msg","用户名不存在!");
                return "login";
            }catch (IncorrectCredentialsException e){
                model.addAttribute("msg","密码错误!");
                return "login";
            }
        }
    
        @RequestMapping("/noauth")
        @ResponseBody
        public String unauthorized(){
            return "未授权无法访问此页面";
        }
    
        @RequestMapping("/logout")
        public String logout(){
            //获取当前用户
            Subject subject = SecurityUtils.getSubject();
            System.out.println(subject.getSession().getAttribute("loginUser"));
            subject.logout(); // session 会销毁,在SessionListener监听session销毁,清理权限缓存
            System.out.println(subject.getSession().getAttribute("loginUser"));
            System.out.println("执行了退出");
            return "login";
        }
    }

    7.ShiroConfig.java

    @Configuration
    public class ShiroConfig {
    
        //ShiroFilterFactoryBean (第三步:连接到前端)
        @Bean
        public ShiroFilterFactoryBean getShiroFilterBean(@Qualifier("securityManager") DefaultWebSecurityManager defaultWebSecurityManager){
            ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
            //设置安全管理器
            bean.setSecurityManager(defaultWebSecurityManager);
    
            //添加shiro的内置过滤器
            /*
            anon: 无需认证即可访问
            authc: 必须认证才能用
            user: 必须拥有 “记住我” 功能才能用
            perms: 拥有对某个资源的权限才能用
            role: 拥有某个角色权限才能访问
            */
    
            Map<String,String> filterMap = new LinkedHashMap<>();
            //拦截
            filterMap.put("/user/add","authc");
            filterMap.put("/user/update","authc");
            //也可使用通配符*
            //filterMap.put("/user/*","authc");
    
            //授权,正常情况下没有授权会跳转到未授权页面
            filterMap.put("/user/add","perms[user:add]");
            filterMap.put("/user/update","perms[user:update]");
    
            bean.setFilterChainDefinitionMap(filterMap);
    
            //若访问时用户未认证,则跳转至登录页面
            bean.setLoginUrl("/toLogin");
            //若访问时用户未被授权,则跳转至未授权页面
            bean.setUnauthorizedUrl("/noauth");
    
            return bean;
        }
    
        //DefaultWebSecurityManager (第二步:管理realm对象)
        @Bean(name="securityManager") //@Bean注解后便被spring托管,不加name属性,默认name值为方法名,这里就加一下吧
        public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
            DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
            //关联UserRealm
            securityManager.setRealm(userRealm);
            return securityManager;
    
        }
    
        //创建realm对象,需要自定义类 (第一步:创建realm对象)
        @Bean(name="userRealm")  //@Bean注解后便被spring托管,不加name属性,默认name值为方法名,这里就加一下吧
        public UserRealm userRealm(){
            return new UserRealm();
        }
    
        //整合ShiroDialect:用来整合shiro thymeleaf
        @Bean
        public ShiroDialect getShiroDialect(){
            return new ShiroDialect();
        }
    }

    8.UserRealm.java

    //自定义UserRealm extends AuthorizingRealm
    public class UserRealm extends AuthorizingRealm {
    
        @Autowired
        UserService userService;
    
    
        //授权
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
            System.out.println("执行了授权");
    
            SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
            //info.addStringPermission("user:add");
    
            //拿到当前登录的对象
            Subject subject = SecurityUtils.getSubject();
            User currentUser = (User) subject.getPrincipal(); //拿到user对象
    
            info.addStringPermission(currentUser.getPerms());
    
            return info;
        }
    
        //认证
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
            System.out.println("执行了认证");
    
            UsernamePasswordToken userToken = (UsernamePasswordToken) token;
    
            /*
            //用户名、密码  模拟从数据库中获取
            String name = "root";
            String password = "1111";
    
            if (!userToken.getUsername().equals(name)){
                return null; //抛出异常 UnknownAccountException
            }
    
            //密码认证,shiro做~
            return new SimpleAuthenticationInfo("",password,"");
            */
    
            //连接真实数据库
            User user = userService.queryUserByName(userToken.getUsername());
            if (user==null){ //没有这个人
                return null; //抛出异常 UnknownAccountException
            }
            Subject currentSubject = SecurityUtils.getSubject();
            Session session = currentSubject.getSession();
            session.setAttribute("loginUser",user);
    
            //可以加密: MD5加密   MD5盐值加密
            //密码认证,shiro做~
            return new SimpleAuthenticationInfo(user,user.getPwd(),"");
    
        }
    }

     9.UserMapper.java

    @Repository
    @Mapper
    public interface UserMapper {
    
        public User queryUserByName(String name);
    
    }

    10.UserMapper.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE mapper
            PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="com.ztx.mapper.UserMapper">
        <select id="queryUserByName" parameterType="String" resultType="User">
            select * from mybatis.user where name=#{name}
        </select>
    </mapper>

    11.User.java

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class User {
    
        private int id;
        private String name;
        private String pwd;
        private String perms;
    }

    12.UserService.java和UserServiceImpl.java

    public interface UserService {
    
        public User queryUserByName(String name);
    
    }
    
    -----------------------------------------------------
    
    @Service
    public class UserServiceImpl implements UserService{
    
        @Autowired
        UserMapper userMapper;
    
        @Override
        public User queryUserByName(String name) {
            return userMapper.queryUserByName(name);
        }
    }

     13.数据库中的user表

    个人对该项目执行流程进行了梳理,如有错误,请指正:

      1.ShiroConf.java中设置好拦截器,规定哪些页面需要用户具备何种要求才可访问,同时还设置当用户不满足要求时应该跳转至什么页面。对应本项目的代码:

      

      2.用户在未认证下,会跳转到登陆页面login.html。输入用户名密码,提交执行controller中的login()方法,该方法内将用户信息封装成token对象,传入subject.login()方法内进行登录验证。对应项目代码:

       

      3.执行登录验证时会跳转到UserRealm类,首先执行认证(doGetAuthenticationInfo):连接数据库判断是否有此人,若没有则返回结果null,若有,则将数据库中的用户信息存储在subject的session中。接着进行密码验证,若验证失败,则将登录不通过返回失败认证信息,反之,返回成功的认证信息,并执行用户授权(doGetAuthorizationInfo)操作,返回授权信息。对应项目代码:

      认证:

       

      授权:

       

      4.本项目中登录成功后跳转到index.html时依旧会再次执行授权操作,因为前端页面需要一个判断展示功能,代码如下:

       

      故控制台信息会是这样:(出现两次授权)

       

      5.之后进入add.html或update.html时,因为会被拦截,所以都要进行授权验证,而此时认证将不会在执行。

     

  • 相关阅读:
    UVA1349 Optimal Bus Route Design 最优巴士路线设计
    POJ3565 Ants 蚂蚁(NEERC 2008)
    UVA1663 Purifying Machine 净化器
    UVa11996 Jewel Magic 魔法珠宝
    NEERC2003 Jurassic Remains 侏罗纪
    UVA11895 Honorary Tickets
    gdb调试coredump(使用篇)
    使用 MegaCLI 检测磁盘状态并更换磁盘
    员工直接坦诚直来直去 真性情
    山东浪潮超越3B4000申泰RM5120-L
  • 原文地址:https://www.cnblogs.com/churujianghudezai/p/12961525.html
Copyright © 2011-2022 走看看