SpringBoot-Shiro

1、Shiro简介

1.1 什么是Shiro?

  • Shiro他是Apache下面的一个java的安全框架。

  • Shiro可以非常容易的开发出足够好的应用,其不仅可以用在javaSE环境,也可以用在JavaEE环境。

  • Shiro可以完成,认证,授权,加密,会话管理,Web集成,缓存等。

2.环境搭配

2.1创建一个Springboot项目

  1. 引入相关依赖jar包

    <?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.3.4.RELEASE</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
        <groupId>com.ddf</groupId>
        <artifactId>shiro-springboot</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <name>shiro-springboot</name>
        <description>Demo project for Spring Boot</description>
    
        <properties>
            <java.version>1.8</java.version>
        </properties>
    
        <dependencies>
    
            <!--引入shiro
            Subject  用户
            SecurityManager  管理所有用户
            Reolm       连接数据
            -->
    
            <!--引入shiro整合Spring的包-->
            <dependency>
                <groupId>org.apache.shiro</groupId>
                <artifactId>shiro-spring</artifactId>
                <version>1.4.1</version>
            </dependency>
            <!--引入web-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <!--引入thymeleaf-->
            <dependency>
                <groupId>org.thymeleaf</groupId>
                <artifactId>thymeleaf-spring5</artifactId>
            </dependency>
            <dependency>
                <groupId>org.thymeleaf.extras</groupId>
                <artifactId>thymeleaf-extras-java8time</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>
        </dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                </plugin>
            </plugins>
        </build>
    </project>
    
  2. 创建Controller类测试项目是否能够运行

    1.新建index.html

    <!DOCTYPE html>
    <html lang="en" xmlns:th="http://www.thymeleaf.org">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    
    <h1>首页</h1>
    <p th:text="${msg}"></p>
    <hr>
    <a th:href="@{/user/add}">add</a> | <a th:href="@{/user/update}">update</a>
    </body>
    </html>
    

    add.html

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

    update.html

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

    login.html

    <!DOCTYPE html>
    <html lang="en" xmlns:th="http://www.thymeleaf.org">
    <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}">
        <p>用户名:<input type="text" name="username" /></p>
        <p>密码:<input type="text" name="password" /></p>
        <p><input type="submit" name="登录" /></p>
    </form>
    </body>
    </html>
    

    2.创建MyController类:

    package com.ddf.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;
    
    @Controller
    public class MyController {
    
        @RequestMapping({"/","/index"})
        public String toIndex(Model model){
            model.addAttribute("msg","Hello Shiro");
            return "index";
        }
    
        @RequestMapping("/user/add")
        public String toAdd(){
            return "user/add";
        }
    
        @RequestMapping("/user/update")
        public String toUpdate(){
            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 {
                //执行登录的方法,如果没有异常就说明ok了
                subject.login(token);
                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 "未授权无法访问此页面";
        }
    }
    

2.2创建配置类

  1. 创建UserRealm类继承AuthorizingRealm获取授权和认证的权限

    package com.ddf.config;
    
    import org.apache.shiro.authc.AuthenticationException;
    import org.apache.shiro.authc.AuthenticationInfo;
    import org.apache.shiro.authc.AuthenticationToken;
    import org.apache.shiro.authz.AuthorizationInfo;
    import org.apache.shiro.realm.AuthorizingRealm;
    import org.apache.shiro.subject.PrincipalCollection;
    
    public class UserRealm extends AuthorizingRealm {
    
        //授权
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
            System.out.println("执行了=》授权doGetAuthorizationInfo");
            return null;
        }
    
        //认证
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
            System.out.println("执行了=》doGetAuthenticationInfo");
            return null;
        }
    }
    
  2. 创建ShiroConfig类(把UserRealm注入到Bean中)

    package com.ddf.config;
    
    import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
    import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration
    public class ShiroConfig {
    
        //第一步:创建realm对象,需要定义类
        @Bean
        public UserRealm userRealm(){
            return new UserRealm();
        }
    
    
        //第二步:DefaultWebSecurityManager
        @Bean(name = "securityManager")
        public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
            DefaultWebSecurityManager securityManager= new DefaultWebSecurityManager();
    
            //关联UserRealm
            securityManager.setRealm(userRealm);
    
            return securityManager;
        }
    
        //第三步:ShiroFileFactoryBean
        @Bean
        public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager defaultWebSecurityManager){
            ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
    
            //设置安全管理器
            bean.setSecurityManager(defaultWebSecurityManager);
            
            //添加shiro的内置过滤器
            /*
            * anon:无需认证就可以访问
            * authc:必须认证了才能访问
            * user:必须拥有 记住我 功能才能用
            * perms:拥有对某个资源的权限才能访问
            * role:拥有某个角色权限才能访问
            * */
            //登录拦截
            Map<String,String> fileMap = new LinkedHashMap<>();
    //        fileMap.put("/user/add","authc");
    //        fileMap.put("/user/update","authc");
            //请求多个认证可使用/*
            fileMap.put("/user/*","authoc");
            bean.setFilterChainDefinitionMap(fileMap);
    
            //设置登录请求
            bean.setLoginUrl("/toLogin");
            
            return bean;
        }
    }
    
  3. 设置需要认证的用户,如果未认证返回login页面

    //登录拦截
    Map<String,String> fileMap = new LinkedHashMap<>();
    //        fileMap.put("/user/add","authc");
    //        fileMap.put("/user/update","authc");
    //请求多个认证可使用/*
    fileMap.put("/user/*","authoc");
    bean.setFilterChainDefinitionMap(fileMap);
    //设置登录请求
    bean.setLoginUrl("/toLogin");
    
  4. 通过login页面获取登录用户信息,判断用户的账号和密码是否正确

    @RequestMapping("/login")
    public String login(String username,String password,Model model){
        //获取当前的用户
        Subject subject = SecurityUtils.getSubject();
        //封装用户的登录数据
        UsernamePasswordToken token = new UsernamePasswordToken(username, password);
    
        try {
            //执行登录的方法,如果没有异常就说明ok了
            subject.login(token);
            return "index";
        }catch (UnknownAccountException e){ //用户名错误
            model.addAttribute("msg","用户名错误");
            return "login";
        }catch (IncorrectCredentialsException e){   //密码不存在
            model.addAttribute("msg","密码错误");
            return "login";
        }
    
  5. 通过自定义的UserRealm认证方法进行用户的一个身份认证

    //认证
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
            System.out.println("执行了=》doGetAuthenticationInfo");
    
            //设置用户名和密码    数据库中取
            String name = "root";
            String password = "123";
    
            UsernamePasswordToken userToken = (UsernamePasswordToken)token;
            //判断登录的用户是否等于当前的用户
            if(!userToken.getUsername().equals(name)){
                return null;    //抛出异常 UnknownAccountException
            }
            return new SimpleAuthenticationInfo("",password,"");
        }
    

3.使用Shiro整合Mybatis

  1. 引入相关的jar包

    <!--Druid-->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>1.1.21</version>
    </dependency>
    <!--log4j-->
    <dependency>
        <groupId>log4j</groupId>
        <artifactId>log4j</artifactId>
        <version>1.2.17</version>
    </dependency>
    <!--mysql-->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <scope>runtime</scope>
    </dependency>
    
  2. 配置application.yml文件(配置druid数据源)

    spring:
      datasource:
        username: root
        password: 123
        #假如时区报错误了,就增加一个时区的配置就OK了, serverTimezone=UTC
        url: jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC&userUnicode=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: 30000
        validationQuery: true
        testOnBorrow: false
        testOnReturn: false
        poolPreparedStatements: true
    
        #配置监控统计拦截的filters,stat:监控统计,Log4j:日志记录,wall:防御sql注入
        #如果允许时报错 java.Lang.ClassNotFoundException: org.apache.Log4j.Priority
        #则导入 Log4j 依赖即可,Maven 地址:https://mvnrepository.com/ortifact/logyj/log4j
        filters: stat,wall,log4j
        maxPoolPreparedStatementPerConnectionSize: 20
        userGlobelDataSourceStat: true
        connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
    
  3. 创建实体类和mapper接口

    1.User实体类:

    package com.ddf.pojo;
    
    public class User {
        private Integer id;
        private String name;
        private String pwd;
    
        public User(){}
    
        public User(Integer id, String name, String pwd) {
            this.id = id;
            this.name = name;
            this.pwd = pwd;
        }
    
        public Integer getId() {
            return id;
        }
    
        public void setId(Integer id) {
            this.id = id;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public String getPwd() {
            return pwd;
        }
    
        public void setPwd(String pwd) {
            this.pwd = pwd;
        }
    }
    

    2.UserMapper

    package com.ddf.mapper;
    
    import com.ddf.pojo.User;
    import org.apache.ibatis.annotations.Mapper;
    import org.springframework.stereotype.Repository;
    
    @Repository
    @Mapper
    public interface UserMapper {
    
        public User queryUserByName(String name);
    }
    

    3.UserService

    package com.ddf.service;
    
    import com.ddf.pojo.User;
    import org.springframework.stereotype.Service;
    
    public interface UserService {
    
        public User queryUserByName(String name);
    }
    

    4.UserServiceImpl

    package com.ddf.service;
    
    import com.ddf.mapper.UserMapper;
    import com.ddf.pojo.User;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    
    @Service
    public class UserServiceImpl implements UserService {
    
        @Autowired
        private UserMapper userMapper;
    
        @Override
        public User queryUserByName(String name) {
            return userMapper.queryUserByName(name);
        }
    }
    

    5.在resource目录下配置mapper.xml文件

    ​ 1. 创建UserMapper.xml

    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE mapper
            PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="com.ddf.mapper.UserMapper">
        <!--加入<cache/>表示开始缓存-->
        <select id="queryUserByName" parameterType="String" resultType="User">
            select * from user where name=#{name}
        </select>
    </mapper>
    

    ​ 2.配置application.properties文件

    #配置mybatis的配置文件
    mybatis.type-aliases-package=com.ddf.pojo
    mybatis.mapper-locations=classpath:mapper/*.xml
    

    ​ 3.测试用户是否能被查到

    package com.ddf.shirospringboot;
    
    import com.ddf.service.UserService;
    import org.junit.jupiter.api.Test;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.context.SpringBootTest;
    
    @SpringBootTest
    class ShiroSpringbootApplicationTests {
    
        @Autowired
        UserService userService;
    
        @Test
        void contextLoads() {
            System.out.println(userService.queryUserByName("张三"));
        }
    }
    

4.使用数据库用户名替换自定义的用户名

  1. UserRealm

    package com.ddf.config;
    
    import com.ddf.pojo.User;
    import com.ddf.service.UserService;
    import org.apache.shiro.authc.*;
    import org.apache.shiro.authz.AuthorizationInfo;
    import org.apache.shiro.realm.AuthorizingRealm;
    import org.apache.shiro.subject.PrincipalCollection;
    import org.springframework.beans.factory.annotation.Autowired;
    
    public class UserRealm extends AuthorizingRealm {
    
        @Autowired
        UserService userService;
    
        //授权
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
            System.out.println("执行了=》授权doGetAuthorizationInfo");
    
            //给用户授权
            SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
            info.addStringPermission("user:add");
    
            //拿到当前登录的这个对象
            Subject subject = SecurityUtils.getSubject();
            //拿到User对象
            User currentUser = (User) subject.getPrincipal();
            info.addStringPermission(currentUser.getPerms());
    
            return info;
        }
    
        //认证
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
            System.out.println("执行了=》doGetAuthenticationInfo");
    
            UsernamePasswordToken userToken = (UsernamePasswordToken)token;
            //自定义用户时可使用这个测试
    //        String name = "root";
    //        String password = "123";
            //判断登录的用户是否等于当前的用户
            /*if(!userToken.getUsername().equals(name)){
                return null;    //抛出异常 UnknownAccountException
            }
            return new SimpleAuthenticationInfo("",password,"");*/
            //连接真实的数据库
            User user = userService.queryUserByName(userToken.getUsername());
    
            if(user==null){ //没有这个用户
                return null;
            }
            //可以加密:  MD5:e10adc3949ba59abbe56e057f20f883e
            //MD5盐值加密:e10adc3949ba59abbe56e057f20f883eusername
            //密码认证,shiro做,加密了
            return new SimpleAuthenticationInfo("",user.getPwd(),"");
        }
    }
    

    2.在ShiroConfig的getShiroFilterFactoryBean方法中添加授权,未授权的跳转至未授权页面

    //第三步:ShiroFileFactoryBean
        @Bean
        public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager defaultWebSecurityManager){
            ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
    
            //设置安全管理器
            bean.setSecurityManager(defaultWebSecurityManager);
            //添加shiro的内置过滤器
            /*
            * anon:无需认证就可以访问
            * authc:必须认证了才能访问
            * user:必须拥有 记住我 功能才能用
            * perms:拥有对某个资源的权限才能访问
            * role:拥有某个角色权限才能访问
            * */
            //登录拦截
            Map<String,String> fileMap = new LinkedHashMap<>();
    
    //        fileMap.put("/user/add","authc");
    //        fileMap.put("/user/update","authc");
            //请求多个认证可使用/*
            //fileMap.put("/user/*","authoc");
            //添加授权  //正常情况下没有授权就会调到未授权的页面去
            fileMap.put("/user/add","perms[1]");
            fileMap.put("/user/update","perms[2]");
            bean.setFilterChainDefinitionMap(fileMap);
    
            //设置登录请求
            bean.setLoginUrl("/toLogin");
            //跳转至未授权页面
            bean.setUnauthorizedUrl("/noauth");
            return bean;
        }
    

5.shiro整合thymeleaf

  1. 引入相关的jar包

    <!--引入shiro和thymeleaf的整合包-->
    <dependency>
    <groupId>com.github.theborakompanioni</groupId>
    <artifactId>thymeleaf-extras-shiro</artifactId>
    <version>2.0.0</version>
    </dependency>
    
  2. 在ShiroConfig中整合ShiroDialect,把它放入bean中,用来整合:shiro thymeleaf

    //整合ShiroDialect:用来整合Shiro thymeleaf
        @Bean
        public ShiroDialect getShiroDialect(){
            return new ShiroDialect();
        }
    
    
  3. 用户有那个页面的权限就显示那个页面

    1.引入shiro 的头文件

    <html lang="en" xmlns:th="http://www.thymeleaf.org"
          xmlns:shiro="http://www.thymeleaf.org/thymeleaf-extras-shiro">
    

    2.修改index首页使用th:hasPermission进行判断

    <!DOCTYPE html>
    <html lang="en" xmlns:th="http://www.thymeleaf.org"
          xmlns:shiro="http://www.thymeleaf.org/thymeleaf-extras-shiro">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    <h1>首页</h1>
    <!--从session中判断值-->
    <div th:if="${session.loginUser==null}">
        <a th:href="@{/toLogin}">登录</a>
    </div>
    
    <p th:text="${msg}"></p>
    <hr>
    <div shiro:hasPermission="1">
        <a th:href="@{/user/add}">add</a>
    </div>
    <div shiro:hasPermission="2">
        <a th:href="@{/user/update}">update</a>
    </div>
    </body>
    </html>
    

    3.登录成功就传一个session对象,在认证中传一个user对象

    //认证
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
            System.out.println("执行了=》doGetAuthenticationInfo");
    
            UsernamePasswordToken userToken = (UsernamePasswordToken)token;
            //自定义时可使用这个测试
    //        String name = "root";
    //        String password = "123";
            //判断登录的用户是否等于当前的用户
            /*if(!userToken.getUsername().equals(name)){
                return null;    //抛出异常 UnknownAccountException
            }
            return new SimpleAuthenticationInfo("",password,"");*/
            //连接真实的数据库
            User user = userService.queryUserByName(userToken.getUsername());
    
            if(user==null){ //没有这个用户
                return null;
            }
    
            Subject currentSubject = SecurityUtils.getSubject();
            Session session = currentSubject.getSession();
            session.setAttribute("loginUser",user);
    
            //可以加密:  MD5:e10adc3949ba59abbe56e057f20f883e
            //MD5盐值加密:e10adc3949ba59abbe56e057f20f883eusername
            //密码认证,shiro做,加密了
            return new SimpleAuthenticationInfo(user,user.getPwd(),"");
        }
    

    4.通过user对象就可以在前端页面进行一个判断,如果用户登录成功就不显示登录按钮

    <!--从session中判断值-->
    <div th:if="${session.loginUser==null}">
        <a th:href="@{/toLogin}">登录</a>
    </div>
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值