zoukankan      html  css  js  c++  java
  • SpringBoot整合Shiro完成认证

    三、SpringBoot整合Shiro思路

    image-20200812095527218

    首先从客户端发来的所有请求都经过Shiro过滤器,如果用户没有认证的都打回去进行认证,认证成功的,再判断是否具有访问某类资源(公有资源,私有资源)的权限,如果没有权限,访问失败;如果有权限访问成功。注意:客户端传来的token要和realm中的认证信息进行相同规则的比较(加密算法要一致)。

    shiro常见的过滤器

    Filter NameClass功能
    anonorg.apache.shiro.web.filter.authc.AnonymousFilter指定url可以匿名访问
    authcorg.apache.shiro.web.filter.authc.FormAuthenticationFilter指定url需要form表单登录,默认会请求username、password。rememberMe等参数并尝试登录,如果登录不了就会跳转到loginUrl配置的路径。我们也可以用这个过滤器设置默认的路径。
    authcBasicorg.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter指定url需要http认证
    authcBearerorg.apache.shiro.web.filter.authc.BearerHttpAuthenticationFilter需要用户认证成功才能访资源否则就通过http请求登录
    logoutorg.apache.shiro.web.filter.authc.LogoutFilter登出过滤器,配置URL实现退出登录的功能。
    noSessionCreationorg.apache.shiro.web.filter.session.NoSessionCreationFilter禁止创建会话
    permsorg.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter指定权限才能访问
    portorg.apache.shiro.web.filter.authz.PortFilter指定端口才能访问
    restorg.apache.shiro.web.filter.authz.HttpMethodPermissionFilter将HTPP请求方法转化成权限字符串
    rolesorg.apache.shiro.web.filter.authz.RolesAuthorizationFilter指定角色才能访问
    sslorg.apache.shiro.web.filter.authz.SslFilter需要https请求才能访问
    userorg.apache.shiro.web.filter.authc.UserFilter需要已登录或者记住我的用户才能访问

    SpringBoot整合Shiro实现认证

    没有加密版

    1、首先创建SpringBoot应用,勾选web模块

    2、然后导入SpringBoot与shiro整合的依赖

    <dependencies>
        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-jasper</artifactId>
        </dependency>
        <dependency>
            <groupId>jstl</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
        </dependency>
    
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--引入SpringBoot与shiro整合的依赖-->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring-boot-starter</artifactId>
            <version>1.5.3</version>
        </dependency>
    
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </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>
    

    3、创建Shiro的配置类。

    @Configuration
    public class ShiroConfig {
    
        @Bean
        public ShiroFilterFactoryBean getShiroFilterFactoryBean(DefaultWebSecurityManager defaultWebSecurityManager) {
            // 创建ShiroFilterFactoryBean
            ShiroFilterFactoryBean filterFactoryBean = new ShiroFilterFactoryBean();
            //设置安全管理器
            filterFactoryBean.setSecurityManager(defaultWebSecurityManager);
    
            //配置受限资源,index是受限资源,authc
            Map<String, String> map = new HashMap<String, String>();
            // /**代表匹配所有url
            map.put("/**", "authc");
            // /user/login 是可以匿名访问的也就是公有资源
            map.put("/user/login", "anon");
            filterFactoryBean.setFilterChainDefinitionMap(map);
    
            // 设置默认认证路径 其实shiro默认的认证路径就是login.jsp
            filterFactoryBean.setLoginUrl("/login.jsp");
    
            return filterFactoryBean;
    
        }
    
        @Bean
        public DefaultWebSecurityManager getDefaultWebSecurityManager(Realm realm) {
            DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
            defaultWebSecurityManager.setRealm(realm);
            return defaultWebSecurityManager;
        }
    
        @Bean
        public Realm getRealm() {
            CustomRelam customRelam = new CustomRelam();
            return customRelam;
        }
    
    }
    

    4、创建自定义realm。

    import org.apache.shiro.authc.AuthenticationException;
    import org.apache.shiro.authc.AuthenticationInfo;
    import org.apache.shiro.authc.AuthenticationToken;
    import org.apache.shiro.authc.SimpleAuthenticationInfo;
    import org.apache.shiro.authz.AuthorizationInfo;
    import org.apache.shiro.realm.AuthorizingRealm;
    import org.apache.shiro.subject.PrincipalCollection;
    
    public class CustomRelam extends AuthorizingRealm {
        // 授权
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
            return null;
        }
    
        //认证
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
            String principal = (String) authenticationToken.getPrincipal();
            // 实际开发中 先确认传来的token用户名是否存在,如果存在,然后根据有户名查询密码返回,进而判断密码是否正确
            
            // 这里假设从数据库中查到的用户名是 xiaozhang
            String username = "xiaozhang";
            if (username.equals(principal)) {
                // 密码还没加密,下一步会加盐并加密
                SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(principal,
                        "123", this.getName());
                // 用户名和面都判断完成后可以返回
                return simpleAuthenticationInfo;
            }
    
            return null;
        }
    }
    
    

    5、编写controller,处理登录和退出的请求

    package club.qy.datao.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.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    
    /**
     * @author 胡
     */
    @Controller
    @RequestMapping("/user")
    public class UserController {
    	// 退出操作
        @RequestMapping("/logout")
        public String logout() {
            Subject subject = SecurityUtils.getSubject();
            subject.logout();
            return "redirect:/login.jsp";
        }
    	// 登录请求
        @PostMapping("/login")
        public String login(String username, String password) {
            Subject subject = SecurityUtils.getSubject();
            UsernamePasswordToken passwordToken = new UsernamePasswordToken(username, password);
            try {
                subject.login(passwordToken);
                return "redirect:/index.jsp";
            } catch (UnknownAccountException e) {
                System.out.println("用户名错误" + e.getMessage());
    
            } catch (IncorrectCredentialsException e) {
                System.out.println("密码错误" + e.getMessage());
            }
    
            return "redirect:/login.jsp";
    
        }
    
    }
    

    下面开始连接数据库MySQLMD5+salt验证登录的认证

    数据库表的创建

    DROP TABLE IF EXISTS `t_user`;
    CREATE TABLE `t_user` (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `username` varchar(40) DEFAULT NULL,
      `password` varchar(40) DEFAULT NULL,
      `salt` varchar(40) DEFAULT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4;
    
    

    注册页面

    <%@page contentType="text/html; UTF-8" pageEncoding="UTF-8" %>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport"
              content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>Document</title>
    </head>
    <body>
            <h1 class="">注册页</h1>
            <form action="${pageContext.request.contextPath}/user/register" method="post">
                用户名:<input type="text" name="username"><br/>
                密码: <input type="password" name="password"><br/>
                 <input type="submit" value="注册">
            </form>
    
    </body>
    </html>
    

    导入依赖

    <dependencies>
        <dependency>
            <groupId>org.apache.tomcat.embed</groupId>
            <artifactId>tomcat-embed-jasper</artifactId>
        </dependency>
        <dependency>
            <groupId>jstl</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
        </dependency>
    
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--引入SpringBoot与shiro整合的依赖-->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring-boot-starter</artifactId>
            <version>1.5.3</version>
        </dependency>
    
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
    
        <!--mysql-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.8</version>
        </dependency>
        <!--mybatis-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.1.2</version>
        </dependency>
        <!--druid-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.19</version>
        </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>
    

    配置文件

    server.port=8081
    server.servlet.context-path=/shiro
    spring.application.name=shiro
    
    spring.mvc.view.suffix=.jsp
    spring.mvc.view.prefix=/
    
    spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
    spring.datasource.url=jdbc:mysql://localhost:3309/shiro?characterEncoding=UTF-8
    spring.datasource.driver-class-name=com.mysql.jdbc.Driver
    spring.datasource.username=root
    spring.datasource.password=123456
    
    mybatis.type-aliases-package=club.qy.datao.entity
    mybatis.mapper-locations=classpath:mapper/*.xml
    

    创建实体类

    @Data
    @Accessors(chain = true)
    @AllArgsConstructor
    @NoArgsConstructor
    public class User {
    
        private String username;
        private String password;
        private Integer id;
        private String salt;
    
    }
    

    创建UserDao

    @Repository
    @Mapper
    public interface UserDao {
        void saveUser(User user);
    }
    

    UserDaoMapper.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="club.qy.datao.dao.UserDao">
        <insert id="saveUser" parameterType="club.qy.datao.entity.User" useGeneratedKeys="true" keyProperty="id">
            insert into t_user values (#{id},#{username},#{password},#{salt})
        </insert>
    </mapper>
    

    service实现

    @Service
    @Transactional
    public class UserServiceImpl implements UserService {
        @Autowired
        private UserDao userDao;
    
        @Override
        public void register(User user) {
    
            //1。生成随机盐
            String saltString = SaltUtil.getSaltString(9);
            //2、给用户设置盐
            user.setSalt(saltString);
            //3、明文密码进行散列算法1024次,并加盐
            Md5Hash md5Hash = new Md5Hash(user.getPassword(), saltString, 1024);
            //4、将转成16进制的密码封装到user对象中
            user.setPassword(md5Hash.toHex());
            //5、插入数据库
            userDao.saveUser(user);
    
        }
    }
    

    SaltUtil工具类

    public class SaltUtil {
    
        public static String getSaltString(int n){
    
            StringBuilder stringBuilder = new StringBuilder();
            char[] array = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ!@~><?abcdefghijklmnopqrstuvwxyz".toCharArray();
            for (int i = 0; i < n; i++) {
                char ch = array[new Random().nextInt(array.length)];
                stringBuilder.append(ch);
            }
            return stringBuilder.toString();
        }
    
    }
    

    ShiroConfig类

    @Configuration
    public class ShiroConfig {
    
        @Bean
        public ShiroFilterFactoryBean getShiroFilterFactoryBean(DefaultWebSecurityManager defaultWebSecurityManager) {
            // 创建ShiroFilterFactoryBean
            ShiroFilterFactoryBean filterFactoryBean = new ShiroFilterFactoryBean();
            //设置安全管理器
            filterFactoryBean.setSecurityManager(defaultWebSecurityManager);
    
            //配置受限资源,index是受限资源,authc
            Map<String, String> map = new HashMap<String, String>();
            // /**代表匹配所有url
            map.put("/**", "authc");
            // /user/login 是可以匿名访问的也就是公有资源
            map.put("/user/**", "anon");
            map.put("/register.jsp", "anon");
            filterFactoryBean.setFilterChainDefinitionMap(map);
    
            // 设置默认认证路径 其实shiro默认的认证路径就是login.jsp
            filterFactoryBean.setLoginUrl("/login.jsp");
            return filterFactoryBean;
        }
         @Bean
        public DefaultWebSecurityManager getDefaultWebSecurityManager(Realm realm) {
            DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
            defaultWebSecurityManager.setRealm(realm);
            return defaultWebSecurityManager;
        }
    
        @Bean
        public Realm getRealm() {
            CustomRelam customRelam = new CustomRelam();
            return customRelam;
        }
    }
    

    image-20200813153209038

  • 相关阅读:
    设计模-设计原则-开闭原则
    使用export/import导出和导入docker容器
    Docker学习笔记(二)
    redis-cli的一些有趣也很有用的功能
    Redis命令总结
    使用domain模块捕获异步回调中的异常
    大话程序猿眼里的高并发
    使用pm2躺着实现负载均衡
    Request —— 让 Node.js http请求变得超简单
    避免uncaughtException错误引起node.js进程崩溃
  • 原文地址:https://www.cnblogs.com/dataoblogs/p/14121883.html
Copyright © 2011-2022 走看看