Spirng Boot+Shiro+Druid+Mybatis Plus+Mysql搭建基础框架实现登录用户认证


开发环境

  1. JDK1.8
  2. Intellij IDEA 2019.1
  3. Win 10 家庭版

项目创建

  1. 打开idea Create New Project
    在这里插入图片描述
  2. 选择Spring Initializr
    在这里插入图片描述
  3. 录入项目名
    在这里插入图片描述
  4. 选择初始化的引入包
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    自动构建的代码结构,框住的文件为非必要文件,可以删除,我开发中都是直接删掉的

初始的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>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.6.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>demo</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </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>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

配置数据库,连接池信息

1.引入Durid+Mybatis Plus+Mysql

引入Druid数据库连接池

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>RELEASE</version>
        </dependency>

引入Mybatis Plus

        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>RELEASE</version>
        </dependency>

引入Mysql驱动

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

2. 配置 连接池及数据源

applicaiton.yml增加下列配置
注意、初始化生成的application.properties,有可以直接在application.properties增加如下配置

spring:
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    url: jdbc:mysql://localhost:3306/test?characterEncoding=utf8&useSSL=true&serverTimezone=GMT%2B8
    username: root
    password: root
    driver-class-name: com.mysql.cj.jdbc.Driver
    druid:
      filters: stat,wall,log4j2,config
      #      连接池最大数
      max-active: 20
      #      第一次初始化链接数
      initial-size: 5
      #      最小空闲数
      min-idle: 5
      validation-query: select '*'
      test-while-idle: true

3. 配置mybatis plus

mybatis-plus:
  global-config:
    #    屏蔽mp banner图
    banner: false

也可以不关闭banner图,我实在觉得它碍眼?

在启动类增加注解

@MapperScan("com.example.demo.**.mapper")

注意:该处有一个坑,不能把路径扫描到com.example.demo.**
无法正确扫描的Mapper,必须是在**.后面增加你的实际放Dao的包名,这里我的包名都是**.mapper,你可以用其他包名

在这里插入图片描述
该注解作用是用作扫描Dao接口

4. 引入工具包

        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-collections4</artifactId>
            <version>RELEASE</version>
        </dependency>

编写登录测试代码

User 用户类

package com.example.demo.login.model;

import lombok.Data;

/**
 * 用户信息类
 * com.example.demo.login.model
 * User
 *
 * @author LiuJingPing
 * @date 2019/7/31 10:53
 */
@Data
public class User {
    private String username;
    private String password;
}

UserMapper

package com.example.demo.login.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.demo.login.model.User;

/**
 * 用户Mapper
 * com.example.demo.login.mapper
 * UserMapper
 *
 * @author LiuJingPing
 * @date 2019/7/31 10:55
 */
public interface UserMapper extends BaseMapper<User> {
}

UserService

package com.example.demo.login.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.example.demo.login.model.User;

/**
 * 用户Service
 * com.example.demo.login.service
 * UserService
 *
 * @author LiuJingPing
 * @date 2019/7/31 10:56
 */
public interface UserService extends IService<User> {
    /**
     * 通过UserName获取用户信息
     *
     * @param username 用户名
     * @return User
     * @author LiuJinPing
     * @date 2019/7/31 10:59
     */
    User getByUsername(String username);
}

UserServiceImpl

package com.example.demo.login.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.demo.login.mapper.UserMapper;
import com.example.demo.login.model.User;
import com.example.demo.login.service.UserService;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * 用户ServiceImpl
 * com.example.demo.login.service.impl
 * UserServiceImpl
 *
 * @author LiuJingPing
 * @date 2019/7/31 10:57
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    @Override
    public User getByUsername(String username) {
        if (StringUtils.isEmpty(username)) {
            return null;
        }
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(User::getUsername, username);
        List<User> list = list(queryWrapper);
        if (CollectionUtils.isNotEmpty(list)) {
            // 从设计上来说,一般用户名是唯一的,所以list的大小一般为1
            return list.get(0);
        }
        return null;
    }
}

LoginController

package com.example.demo.login.controller;

import com.example.demo.login.model.User;
import com.example.demo.login.service.UserService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;

/**
 * 登录控制器
 * com.example.demo.login.controller
 * LoginController
 *
 * @author LiuJingPing
 * @date 2019/7/31 10:54
 */
@Controller
@RequestMapping("/")
public class LoginController {

    private final UserService userService;

    public LoginController(UserService userService) {
        this.userService = userService;
    }

    @PostMapping("/doLogin")
    public String doLogin(User user) {
        if (user != null) {
            //如果账号密码正确跳转到index
            User getUser = userService.getByUsername(user.getUsername());
            if (getUser != null && StringUtils.equals(user.getPassword(), getUser.getPassword())) {
                return "index";
            }
        }
        //否则跳转到密码错误页面
        return "passwordError";
    }

    @RequestMapping("/login")
    public String login() {
        return "login";
    }

    //默认跳转首页
    @RequestMapping("/")
    public String index1() {
        return "login";
    }

    @RequestMapping("/index")
    public String index() {
        return "index";
    }

}

建库 建表 插入测试用户

CREATE DATABASE `test` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci

CREATE TABLE `user`  (
  `username` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `passwrod` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  PRIMARY KEY (`username`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES ('jimbo', '123456');

编写页面

login.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登录测试页面</title>
</head>
<body>
<form method="post" action="/doLogin">
    用户名:<input type="text" name="username"/><br/>
    密码:<input type="text" name="password"/><br/>
    <input type="submit">
</form>
</body>
</html>

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>首页</title>
</head>
<body>
<h1 style="text-align: center">Hello World</h1>
</body>
</html>

passwordError.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>密码错误</title>
</head>
<body>
<h1 style="text-align: center;color:red;">登录失败:用户名或密码错误</h1>
</body>
</html>

启动测试

到目前为止,并未引入Shiro,所以 /index 可以直接访问,现在实现的功能是账号密码正确时,跳转Index 否则跳转passwordError

applicaiton.yml增加启动端口

server:
  port: 8080

启动项目 访问http://localhost:8080/
输入正确的账号,点击提交
在这里插入图片描述
为了方便测试,密码输入框用的明文,实际开发应该使用type=password
在这里插入图片描述
正确跳转到index.html
返回,输入错误的账号密码
在这里插入图片描述
成功跳转到错误页面
到此,基本功能开发完成
但是还未集成Shiro的身份认证
现在,用户可以通过浏览器地址访问http://localhost:8080/index 访问到首页,在实际开发中不允许这样的情况
接下来开始集成Shiro

引入Shiro

pom.xml增加引入

        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-core</artifactId>
            <version>1.4.1</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.4.1</version>
        </dependency>

配置Shiro

自定义realm

package com.example.demo.conifg.shiro;

import com.example.demo.login.model.User;
import com.example.demo.login.service.UserService;
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;
import org.springframework.beans.factory.annotation.Autowired;

/**
 * 自定义Realm
 * com.example.demo.configuration.shiro
 * CustomizeRealm
 *
 * @author LiuJingPing
 * @date 2019/7/31 11:35
 */
public class CustomizeRealm extends AuthorizingRealm {
    @Autowired
    private UserService userService;

    /**
     * 权限认证 暂时不使用
     *
     * @param principals
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
        return null;
    }

    /**
     * 身份认证 登录认证
     *
     * @param token
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
//        获取登录令牌用户名
        String username = (String) token.getPrincipal();
//        获取用户信息
        User user = userService.getByUsername(username);
        if (user == null) {
            throw new AuthenticationException();
        }
        return new SimpleAuthenticationInfo(username, user.getPassword(), "pb");
    }
}

Shiro配置类

package com.example.demo.conifg.shiro;

import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.HashMap;
import java.util.Map;

/**
 * Shiro配置类
 * com.example.demo.configuration.shiro
 * ShiroConfiguration
 *
 * @author LiuJingPing
 * @date 2019/7/31 11:40
 */
@Configuration
public class ShiroConfiguration {

    @Bean("customizeRealm")
    public Realm realm() {
        return new CustomizeRealm();
    }

    @Bean("sessionManager")
    public SessionManager sessionManager() {
        return new DefaultWebSessionManager();
    }

    @Bean("securityManager")
    public SecurityManager securityManager(SessionManager sessionManager, Realm customizeRealm) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setSessionManager(sessionManager);
        securityManager.setRealm(customizeRealm);
        return securityManager;
    }

    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager) {
        ShiroFilterFactoryBean filterFactoryBean = new ShiroFilterFactoryBean();
        //必须设置securityManager
        filterFactoryBean.setSecurityManager(securityManager);
        //增加请求地址的认证
        filterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap());
        //未登录跳转页面
        filterFactoryBean.setLoginUrl("/login");
        //登录成功跳转页面
        filterFactoryBean.setSuccessUrl("/index");
        return filterFactoryBean;
    }

    private Map<String, String> filterChainDefinitionMap() {
        //anon 不需要认证
        //authc 需要认证
        //当访问时是从上往下认证的,当URL匹配到第一个的时候,不会继续向下匹配了
        Map<String, String> filterChainDefinitionMap = new HashMap<>();
        filterChainDefinitionMap.put("/login", "anon");
        filterChainDefinitionMap.put("/doLogin", "anon");
        filterChainDefinitionMap.put("/**", "authc");
        filterChainDefinitionMap.put("/logout", "logout");
        return filterChainDefinitionMap;
    }

}

重写登录方法

    @PostMapping("/doLogin")
    public String doLogin(User user) {
        if (user != null) {
            Subject subject = SecurityUtils.getSubject();
            UsernamePasswordToken token = new UsernamePasswordToken(user.getUsername(), user.getPassword());
            try {
//                如果账号密码都正确不会抛出异常
//                shiro 认证 当认证失败时会抛出异常
                //try catch 处理掉异常
                subject.login(token);
                return "index";
            } catch (Exception e) {
                return "passwordError";
            }
        }
        //否则跳转到密码错误页面
        return "passwordError";
    }

当subject.login()时,会调用我们自定义的realm的doGetAuthenticationInfo方法进行身份认证,认证处理都在realm里面,所以不需要在这里对比密码

启动测试

首先测试,未经过身份认证的是否访问index页面
访问http://localhost:8080/index
因为没有认证的身份,所以会自动重定向到/login,
即在ShiroConfiguration中配置的

        //未登录跳转页面
        filterFactoryBean.setLoginUrl("/login");

在这里插入图片描述
输入账号密码,进行登录测试
跳转页面和之前一致,但是在登录成功后,可直接输入http://localhost:8080/index进行首页访问,在登录失败后,访问该页面会返回到登录页面

总结

  1. springboot 已经简化了spring的配置,在开发中,我们可以减少xml进行配置,在代码中大量使用注解的形式引入bean
  2. 为什么要使用mybatis plus?因为mybatis plus 在mybatis的基础上简化了很多,而却mybatis plus 提供了许多的通用方法,通过继承可减少很多代码的编写,同时mybatis plus提供lambda表达式进行条件编写,在后期若数据库字段改变,我们只需要在实体类上增加注解即可,不用修改业务代码,mybatis plus只在mybatis基础上做了增强,可完全移植。注意:lambda表达式方式只能在jdk1.8及以上使用
  3. Shiro相对SpringSecurity 来说更简单一点,功能也能满足日常使用,Shiro也可通过自定义SessionDao将Session写入Redis进行session共享

如果本文章有什么问题可以提出,我会尽快修改,如果你有什么更好的实现方式,请在文章后留言

最后,附上源码,可直接下载使用idea运行
链接:https://pan.baidu.com/s/12m5_1dGvM7-3etjsmjOcTA
提取码:1n1t

课程简介:历经半个多月的时间,Debug亲自撸的 “企业员工角色权限管理平台” 终于完成了。正如字面意思,本课程讲解的是一个真正意义上的、企业级的项目实战,主要介绍了企业级应用系统中后端应用权限的管理,其中主要涵盖了六大核心业务模块、十几张数据库表。 其中的核心业务模块主要包括用户模块、部门模块、岗位模块、角色模块、菜单模块和系统日志模块;与此同时,Debug还亲自撸了额外的附属模块,包括字典管理模块、商品分类模块以及考勤管理模块等等,主要是为了更好地巩固相应的技术栈以及企业应用系统业务模块的开发流程! 核心技术栈列表: 值得介绍的是,本课程在技术栈层面涵盖了前端和后端的大部分常用技术,包括Spring Boot、Spring MVC、MybatisMybatis-PlusShiro(身份认证与资源授权跟会话等等)、Spring AOP、防止XSS攻击、防止SQL注入攻击、过滤器Filter、验证码Kaptcha、热部署插件Devtools、POI、Vue、LayUI、ElementUI、JQuery、HTML、Bootstrap、Freemarker、一键打包部署运行工具Wagon等等,如下图所示: 课程内容与收益: 总的来说,本课程是一门具有很强实践性质的“项目实战”课程,即“企业应用员工角色权限管理平台”,主要介绍了当前企业级应用系统中员工、部门、岗位、角色、权限、菜单以及其他实体模块的管理;其中,还重点讲解了如何基于Shiro的资源授权实现员工-角色-操作权限、员工-角色-数据权限的管理;在课程的最后,还介绍了如何实现一键打包上传部署运行项目等等。如下图所示为本权限管理平台的数据库设计图: 以下为项目整体的运行效果截图: 值得一提的是,在本课程中,Debug也向各位小伙伴介绍了如何在企业级应用系统业务模块的开发中,前端到后端再到数据库,最后再到服务器的上线部署运行等流程,如下图所示:
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值