zoukankan      html  css  js  c++  java
  • Spring boot后台搭建二集成Shiro实现用户验证

    上一篇文章中介绍了Shiro 查看

    将Shiro集成到spring boot的步骤:

      (1)定义一个ShiroConfig,配置SecurityManager Bean,SecurityManager为Shiro的安全管理器,管理着所有Subject

      (2)在ShiroConfig中配置ShiroFilterFactoryBean,其为Shiro过滤器工厂类,依赖于SecurityManager

      (3)自定义Realm实现,Realm包含doGetAuthorizationInfo()doGetAuthenticationInfo()方法

    实现下用户认证:

      没登录跳转到登录页面;登录后跳转到首页面;注销后,跳转到登录页面

    1.引入依赖

      引入Shiro和thymeleaf依赖

    <!-- thymeleaf -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-thymeleaf</artifactId>
    </dependency>
    
    <!-- shiro-spring -->
    <dependency>
        <groupId>org.apache.shiro</groupId>
        <artifactId>shiro-spring</artifactId>
        <version>1.4.0</version>
    </dependency>

    2.ShiroConfig

       定义一个Shiro配置类,ShiroConfig

    package com.sfn.bms.common.shiro;
    
    import org.apache.shiro.mgt.SecurityManager;
    import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
    import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import java.util.*;
    
    /**
     * Shiro 配置类
     */
    @Configuration
    public class ShiroConfig {
    
        @Bean
        public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
            ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
            // 设置securityManager
            shiroFilterFactoryBean.setSecurityManager(securityManager);
            // 登录的url
            shiroFilterFactoryBean.setLoginUrl("/login");
            // 登录成功后跳转的url
            shiroFilterFactoryBean.setSuccessUrl("/index");
            // 未授权url
            shiroFilterFactoryBean.setUnauthorizedUrl("/403");
    
            LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
    
            // 定义filterChain,静态资源不拦截
            filterChainDefinitionMap.put("/css/**", "anon");
            filterChainDefinitionMap.put("/js/**", "anon");
            filterChainDefinitionMap.put("/fonts/**", "anon");
            filterChainDefinitionMap.put("/img/**", "anon");
            // druid数据源监控页面不拦截
            filterChainDefinitionMap.put("/druid/**", "anon");
            // 配置退出过滤器,其中具体的退出代码Shiro已经替我们实现了
            filterChainDefinitionMap.put("/logout", "logout");
            filterChainDefinitionMap.put("/", "anon");
            // 除上以外所有url都必须认证通过才可以访问,未通过认证自动访问LoginUrl
            filterChainDefinitionMap.put("/**", "authc");
    
            shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
            return shiroFilterFactoryBean;
        }
    
        @Bean
        public SecurityManager securityManager() {
            DefaultWebSecurityManager securityManager =  new DefaultWebSecurityManager();
            securityManager.setRealm(myShiroRealm());
            return securityManager;
        }
    
        @Bean
        public MyShiroRealm myShiroRealm() {
            MyShiroRealm shiroRealm = new MyShiroRealm();
            return shiroRealm;
        }
    
    }

      anonauthc等为Shiro为实现的过滤器

    3.Realm

      Realm进行实现,然后注入到SecurityManager中

      自定义Realm实现只需继承AuthorizingRealm类,然后实现doGetAuthorizationInfo()和doGetAuthenticationInfo()方法即可

      用户认证只实现doGetAuthenticationInfo()

    package com.sfn.bms.common.shiro;
    import com.sfn.bms.system.model.User;
    import com.sfn.bms.system.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;
    import org.springframework.stereotype.Component;
    
    /**
     * 自定义实现 ShiroRealm,包含认证和授权两大模块
     */
    @Component("shiroRealm")
    public class MyShiroRealm extends AuthorizingRealm {
    
        @Autowired
        private UserService userService;
    
        /**
         * 授权模块,获取用户角色和权限
         *
         * @param principal principal
         * @return AuthorizationInfo 权限信息
         */
        @Override
        protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principal) {
    
            return null;
        }
    
        /**
         * 用户认证
         *
         * @param token AuthenticationToken 身份认证 token
         * @return AuthenticationInfo 身份认证信息
         * @throws AuthenticationException 认证相关异常
         */
        @Override
        protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
    
            // 获取用户输入的用户名和密码
            String account = (String) token.getPrincipal();
            String password = new String((char[]) token.getCredentials());
    
            // 通过用户名到数据库查询用户信息
            User user = userService.findByAccount(account);
    
            if (user == null) {
                throw new UnknownAccountException("用户名或密码错误!");
            }
            if (!password.equals(user.getPassword())) {
                throw new IncorrectCredentialsException("用户名或密码错误!");
            }
            if (user.getStatus().equals("0")) {
                throw new LockedAccountException("账号已被锁定,请联系管理员!");
            }
            SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, password, getName());
            return info;
        }
    
    
    }

    4.数据层

      数据表User

      实体类User

    package com.sfn.bms.system.model;
    
    import javax.persistence.*;
    
    public class User {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Short id;
    
        /**
         * 账号
         */
        private String account;
    
        /**
         * 密码
         */
        private String password;
    
        /**
         * 邮箱
         */
        private String email;
    
        /**
         * 状态 1-正常,0-禁用,-1-删除
         */
        private Boolean status;
    
        /**
         * 添加时间
         */
        @Column(name = "create_time")
        private Integer createTime;
    
        /**
         * 上次登陆时间
         */
        @Column(name = "last_login_time")
        private Integer lastLoginTime;
    
        /**
         * 上次登录IP
         */
        @Column(name = "last_login_ip")
        private String lastLoginIp;
    
        /**
         * 登陆次数
         */
        @Column(name = "login_count")
        private Integer loginCount;
    
        /**
         * @return id
         */
        public Short getId() {
            return id;
        }
    
        /**
         * @param id
         */
        public void setId(Short id) {
            this.id = id;
        }
    
        /**
         * 获取账号
         *
         * @return account - 账号
         */
        public String getAccount() {
            return account;
        }
    
        /**
         * 设置账号
         *
         * @param account 账号
         */
        public void setAccount(String account) {
            this.account = account == null ? null : account.trim();
        }
    
        /**
         * 获取密码
         *
         * @return password - 密码
         */
        public String getPassword() {
            return password;
        }
    
        /**
         * 设置密码
         *
         * @param password 密码
         */
        public void setPassword(String password) {
            this.password = password == null ? null : password.trim();
        }
    
        /**
         * 获取邮箱
         *
         * @return email - 邮箱
         */
        public String getEmail() {
            return email;
        }
    
        /**
         * 设置邮箱
         *
         * @param email 邮箱
         */
        public void setEmail(String email) {
            this.email = email == null ? null : email.trim();
        }
    
        /**
         * 获取状态 1-正常,0-禁用,-1-删除
         *
         * @return status - 状态 1-正常,0-禁用,-1-删除
         */
        public Boolean getStatus() {
            return status;
        }
    
        /**
         * 设置状态 1-正常,0-禁用,-1-删除
         *
         * @param status 状态 1-正常,0-禁用,-1-删除
         */
        public void setStatus(Boolean status) {
            this.status = status;
        }
    
        /**
         * 获取添加时间
         *
         * @return create_time - 添加时间
         */
        public Integer getCreateTime() {
            return createTime;
        }
    
        /**
         * 设置添加时间
         *
         * @param createTime 添加时间
         */
        public void setCreateTime(Integer createTime) {
            this.createTime = createTime;
        }
    
        /**
         * 获取上次登陆时间
         *
         * @return last_login_time - 上次登陆时间
         */
        public Integer getLastLoginTime() {
            return lastLoginTime;
        }
    
        /**
         * 设置上次登陆时间
         *
         * @param lastLoginTime 上次登陆时间
         */
        public void setLastLoginTime(Integer lastLoginTime) {
            this.lastLoginTime = lastLoginTime;
        }
    
        /**
         * 获取上次登录IP
         *
         * @return last_login_ip - 上次登录IP
         */
        public String getLastLoginIp() {
            return lastLoginIp;
        }
    
        /**
         * 设置上次登录IP
         *
         * @param lastLoginIp 上次登录IP
         */
        public void setLastLoginIp(String lastLoginIp) {
            this.lastLoginIp = lastLoginIp == null ? null : lastLoginIp.trim();
        }
    
        /**
         * 获取登陆次数
         *
         * @return login_count - 登陆次数
         */
        public Integer getLoginCount() {
            return loginCount;
        }
    
        /**
         * 设置登陆次数
         *
         * @param loginCount 登陆次数
         */
        public void setLoginCount(Integer loginCount) {
            this.loginCount = loginCount;
        }
    }
    View Code

      UserService 

    package com.sfn.bms.system.service;
    
    import com.sfn.bms.common.service.IService;
    import com.sfn.bms.system.model.User;
    
    public interface UserService extends IService<User> {
        User findByAccount(String account);
    }
    View Code

      UserServiceImpl

    package com.sfn.bms.system.service.impl;
    
    import com.sfn.bms.common.service.impl.BaseService;
    import com.sfn.bms.system.model.User;
    import com.sfn.bms.system.service.UserService;
    import org.springframework.stereotype.Repository;
    import tk.mybatis.mapper.entity.Example;
    
    import java.util.List;
    
    @Repository("userService")
    public class UserServiceImpl extends BaseService<User> implements UserService {
        @Override
        public User findByAccount(String account) {
            Example example = new Example(User.class);
            example.createCriteria().andCondition("lower(account)=", account.toLowerCase());
            List<User> list = this.selectByExample(example);
            return list.isEmpty() ? null : list.get(0);
        }
    
    }
    View Code

      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.sfn.bms.system.mapper.UserMapper">
      <resultMap id="BaseResultMap" type="com.sfn.bms.system.model.User">
        <!--
          WARNING - @mbg.generated
        -->
        <id column="id" jdbcType="SMALLINT" property="id" />
        <result column="account" jdbcType="VARCHAR" property="account" />
        <result column="password" jdbcType="CHAR" property="password" />
        <result column="email" jdbcType="VARCHAR" property="email" />
        <result column="status" jdbcType="BIT" property="status" />
        <result column="create_time" jdbcType="INTEGER" property="createTime" />
        <result column="last_login_time" jdbcType="INTEGER" property="lastLoginTime" />
        <result column="last_login_ip" jdbcType="VARCHAR" property="lastLoginIp" />
        <result column="login_count" jdbcType="INTEGER" property="loginCount" />
      </resultMap>
    
    </mapper>
    View Code

    5.前端页面

      login.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <link rel="stylesheet" th:href="@{/css/login.css}" type="text/css">
        <link rel="stylesheet" th:href="@{css/iCheck/minimal/blue.css}" type="text/css">
        <link rel="stylesheet" th:href="@{css/app.css}" type="text/css">
        <script th:src="@{/js/jquery.min.js}"></script>
    </head>
    <body>
    <div class="login-page">
        <!-- Form-->
        <div class="form">
            <div class="form-toggle"></div>
            <div class="form-panel one">
                <div class="form-header">
                    <h1>账户登录</h1>
                </div>
                <div class="form-content">
                    <div class="form-group">
                        <label>用户名</label>
                        <input type="text" name="account" />
                    </div>
                    <div class="form-group">
                        <label>密码</label>
                        <input type="password" name="password" />
                    </div>
                    <div class="form-group">
                        <button onclick="login()" id="loginButton">登录</button>
                    </div>
                </div>
            </div>
        </div>
    
    </div>
    </body>
    <script th:inline="javascript">
        var ctx = [[@{/}]];
            function login() {
                var account= $("input[name='account']").val();
                var password = $("input[name='password']").val();
                $.ajax({
                    type: "post",
                    url: ctx + "login",
                    data: {"account": account,"password": password},
                    dataType: "json",
                    success: function (r) {
                        if (r.code == 0) {
                            location.href = ctx + 'index';
                        } else {
                            alert(r.msg);
                        }
                    }
                });
            }
    </script>
    </html>

      index.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
        <p>你好![[${user.account}]]</p>
        <a th:href="@{/logout}">注销</a>
    </body>
    </html>
    View Code

    6.Controller方法

    LoginController
    package com.sfn.bms.system.controller;
    
    import com.sfn.bms.common.domian.ResponseBo;
    import com.sfn.bms.common.util.MD5Utils;
    import com.sfn.bms.system.model.User;
    import org.apache.shiro.SecurityUtils;
    import org.apache.shiro.authc.*;
    import org.apache.shiro.session.Session;
    import org.apache.shiro.subject.Subject;
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.ResponseBody;
    
    @Controller
    public class LoginController {
    
        @GetMapping("/login")
        public String login() {
            return "login";
        }
    
        @PostMapping("/login")
        @ResponseBody
        public ResponseBo login(String account, String password) {
            // 密码MD5加密
            password = MD5Utils.encrypt(account, password);
            System.out.println(password);
            UsernamePasswordToken token = new UsernamePasswordToken(account, password);
            // 获取Subject对象
            Subject subject = SecurityUtils.getSubject();
            try {
                subject.login(token);
                return ResponseBo.ok();
            } catch (UnknownAccountException e) {
                return ResponseBo.error(e.getMessage());
            } catch (IncorrectCredentialsException e) {
                return ResponseBo.error(e.getMessage());
            } catch (LockedAccountException e) {
                return ResponseBo.error(e.getMessage());
            } catch (AuthenticationException e) {
                return ResponseBo.error("认证失败!");
            }
        }
    
        @RequestMapping("/")
        public String redirectIndex() {
            return "redirect:/index";
        }
    
        @RequestMapping("/index")
        public String index(Model model) {
            // 登录成后,即可通过Subject获取登录的用户信息
            User user = (User) SecurityUtils.getSubject().getPrincipal();
    
            model.addAttribute("user", user);
            return "index";
        }
    }

    7.测试

      启动项目

      浏览器打开

      http://localhost:8080/

      http://localhost:8080/index

      回跳转到http://localhost:8080/login

      输入用户名super密码123456

    点注销,根据ShiroConfig的配置filterChainDefinitionMap.put("/logout", "logout"),Shiro会自动帮我们注销用户信息,并重定向到/路径

    相关代码 地址

  • 相关阅读:
    SQL SERVER将指定表中的指定字段按照(,)逗号分隔
    关于百度 UEditor的使用
    关于jquery的 $("form").serialize()和 new FormData表单序列化
    mvc5 + ef6 + autofac搭建项目(repository+uow)(二)
    (转载)[FFmpeg]使用ffmpeg从各种视频文件中直接截取视频图片
    sql查看数据库表使用情况
    EF FluentAPI映射一对多 关系时候报错
    (转载)Javascript 进阶 作用域 作用域链
    (转载)loadrunner简单使用——HTTP,WebService,Socket压力测试脚本编写
    [moka学习笔记]yii2.0 rules的用法(收集,不定期更新)
  • 原文地址:https://www.cnblogs.com/baby123/p/11064022.html
Copyright © 2011-2022 走看看