SpringBoot+Mybatis-plus:结合JWT规则生成token字符串实现登录的步骤

1,229 阅读5分钟

一、创建SpringBoot项目

二、添加依赖(pom.xml)

<!-- MyBatis Plus代码生成器-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>3.4.0</version>
        </dependency>

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

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

        <!-- velocity 模板引擎, Mybatis Plus 代码生成器需要 -->
        <dependency>
            <groupId>org.apache.velocity</groupId>
            <artifactId>velocity-engine-core</artifactId>
            <version>2.0</version>
        </dependency>

        <!--lombok用来简化实体类:需要安装lombok插件-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        
        <!--swagger-->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>2.7.0</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>2.7.0</version>
        </dependency>
        
        <!-- JWT-->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.7.0</version>
        </dependency>

三、编写配置文件(application.properties)

# 服务端口
server.port=8001

# mysql数据库连接
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3308/tokenlogin?serverTimezone=GMT%2B8
spring.datasource.username=root
spring.datasource.password=root

#配置mapper xml文件的路径
mybatis-plus.mapper-locations=classpath:com/sise/wangzhan/mapper/xml/*.xml

四、数据库创建表

创建语句

-- auto-generated definition
create table member
(
    id           bigint                                 not null comment '会员id'
        primary key,
    openid       varchar(128) charset utf8mb4           null comment '微信openid',
    mobile       varchar(11) charset utf8mb4 default '' null comment '手机号',
    password     varchar(255) charset utf8mb4           null comment '密码',
    nickname     varchar(50) charset utf8mb4            null comment '昵称',
    sex          tinyint(2) unsigned                    null comment '性别 1 女,2 男',
    age          tinyint unsigned                       null comment '年龄',
    avatar       varchar(255) charset utf8mb4           null comment '用户头像',
    sign         varchar(100) charset utf8mb4           null comment '用户签名',
    is_disabled  tinyint(1)                  default 0  not null comment '是否禁用 1(true)已禁用,  0(false)未禁用',
    is_deleted   tinyint(1)                  default 0  not null comment '逻辑删除 1(true)已删除, 0(false)未删除',
    gmt_create   datetime                               not null comment '创建时间',
    gmt_modified datetime                               not null comment '更新时间'
);

五、使用MyBatis-plus自动生成代码

生成代码

package com.sise.wangzhan;

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import com.baomidou.mybatisplus.generator.config.GlobalConfig;
import com.baomidou.mybatisplus.generator.config.PackageConfig;
import com.baomidou.mybatisplus.generator.config.StrategyConfig;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import org.junit.Test;

/**
 *  Copyright: Copyright (c) 2021 Asiainfo
 *  
 *  @ClassName: ucenter.CodeGenerator
 *  @Description: 该类的功能描述
 * <p>
 *  @version: v1.0.0
 *  @author:   wangzhan
 *  @date: 2021/1/5 11:42 
 * <p>
 *  Modification History:
 *  Date       Author    Version    Description
 * ----------------------------------------------------------
 *  2021/1/5    acer     v1.0.0      修改原因
 */
public class CodeGenerator {

    @Test
    public void codeGeneratorTest(){
        // 1、创建代码生成器
        AutoGenerator mpg = new AutoGenerator();

        // 2、全局配置
        GlobalConfig gc = new GlobalConfig();
        String projectPath = System.getProperty("user.dir");
        gc.setOutputDir("\\token_login_demo" + "/src/main/java");

        gc.setAuthor("wangzhan");
        gc.setOpen(false); //生成后是否打开资源管理器
        gc.setFileOverride(false); //重新生成时文件是否覆盖
        gc.setServiceName("%sService");	//去掉Service接口的首字母I
        gc.setIdType(IdType.AUTO); //主键策略
        gc.setDateType(DateType.ONLY_DATE);//定义生成的实体类中日期类型
        gc.setSwagger2(true);//开启Swagger2模式

        mpg.setGlobalConfig(gc);

        // 3、数据源配置
        DataSourceConfig dsc = new DataSourceConfig();
        dsc.setUrl("jdbc:mysql://localhost:3308/wang?serverTimezone=GMT%2B8");
        dsc.setDriverName("com.mysql.cj.jdbc.Driver");
        dsc.setUsername("root");
        dsc.setPassword("root");
        dsc.setDbType(DbType.MYSQL);
        mpg.setDataSource(dsc);

        // 4、包配置
        PackageConfig pc = new PackageConfig();
        // 包    com.sise.wangzhan.generator
        pc.setParent("com.sise");
        pc.setModuleName("wangzhan"); //模块名
        // 包    com.sise.wangzhan.controller
        pc.setController("controller");
        pc.setEntity("entity");
        pc.setService("service");
        pc.setMapper("mapper");
        mpg.setPackageInfo(pc);

        // 5、策略配置
        StrategyConfig strategy = new StrategyConfig();

        // 表名
        strategy.setInclude("member");

        strategy.setNaming(NamingStrategy.underline_to_camel);//数据库表映射到实体的命名策略
        strategy.setTablePrefix(pc.getModuleName() + "_"); //生成实体时去掉表前缀

        strategy.setColumnNaming(NamingStrategy.underline_to_camel);//数据库表字段映射到实体的命名策略
        strategy.setEntityLombokModel(true); // lombok 模型 @Accessors(chain = true) setter链式操作

        strategy.setRestControllerStyle(true); //restful api风格控制器
        strategy.setControllerMappingHyphenStyle(true); //url中驼峰转连字符

        mpg.setStrategy(strategy);

        // 6、执行
        mpg.execute();
    }

}
 

需要注意的地方(需要改成自己的配置)

1、项目路径的修改

2、数据源的配置

3、包的配置

4、数据库中表名的填写

执行前效果

执行后效果

六、Mybatis-plus自动填充时间

1、注解填充字段

2、实现元对象处理器接口

代码如下

package com.sise.wangzhan.servicebase.handler;

import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;

import java.util.Date;

@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
    @Override
    public void insertFill(MetaObject metaObject) {
        //属性名称,不是字段名称
        this.setFieldValByName("createTime", new Date(), metaObject);
        this.setFieldValByName("modifiedTime", new Date(), metaObject);
    }

    @Override
    public void updateFill(MetaObject metaObject) {
        this.setFieldValByName("modifiedTime", new Date(), metaObject);
    }
}

七、需要使用swagger进行配合测试

编写配置类

package com.sise.wangzhan.config;

import com.google.common.base.Predicates;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

/**
 * @Classname SwaggerConfig
 * @Description TODO    1 访问地址  http://localhost:端口号/swagger-ui.html
 * @Date 2020/5/30 11:20
 * @Created by wangzhan
 */
@Configuration  // 配置类
@EnableSwagger2 // swagger注解
public class SwaggerConfig {

    // swagger插件
    @Bean
    public Docket webApiConfig(){

        return new Docket(DocumentationType.SWAGGER_2)
                .groupName("webApi")
                .apiInfo(webApiInfo())
                .select()
                //.paths(Predicates.not(PathSelectors.regex("/admin/.*")))
                .paths(Predicates.not(PathSelectors.regex("/error.*")))
                .build();

    }

    private ApiInfo webApiInfo(){

        return new ApiInfoBuilder()
                .title("网站-测试API文档")
                .description("本文档没有什么描述")
                .version("1.0")
                .contact(new Contact("java", "http://atguigu.com", "1123@qq.com"))
                .build();
    }

}

八、添加JWT工具类

代码如下

package com.sise.wangzhan.utils;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.util.StringUtils;

import javax.servlet.http.HttpServletRequest;
import java.util.Date;

/**
 * @author wangzhan
 * @since 2019/10/16
 */
public class JwtUtils {

    //常量
    public static final long EXPIRE = 1000 * 60 * 60 * 24; //token过期时间, 这里设置的是一天的有效时间
    public static final String APP_SECRET = "ukc8BDbRigUDaY6pZFfWus2jZWLPHO"; //秘钥,自己随便设

    //生成token字符串的方法
    public static String getJwtToken(BigInteger id, String nickname){

        String JwtToken = Jwts.builder()
                // JWT的头信息
                .setHeaderParam("typ", "JWT")
                .setHeaderParam("alg", "HS256")

                // 设置过期时间
                .setSubject("onlineCourse-user")
                .setIssuedAt(new Date())
                .setExpiration(new Date(System.currentTimeMillis() + EXPIRE))

                // 数据库中的字段
                .claim("id", id)  //设置token主体部分 ,存储用户信息
                .claim("nickname", nickname)

                // 签名哈希
                .signWith(SignatureAlgorithm.HS256, APP_SECRET)
                .compact();

        return JwtToken;
    }

    /**
     * 判断token是否存在与有效
     * @param jwtToken
     * @return
     */
    public static boolean checkToken(String jwtToken) {
        if(StringUtils.isEmpty(jwtToken)) return false;
        try {
            Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * 判断token是否存在与有效
     * @param request
     * @return
     */
    public static boolean checkToken(HttpServletRequest request) {
        try {
            String jwtToken = request.getHeader("token");
            if(StringUtils.isEmpty(jwtToken)) return false;
            Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * 根据token字符串获取会员id
     * @param request
     * @return
     */
    public static String getMemberIdByJwtToken(HttpServletRequest request) {
        String jwtToken = request.getHeader("token");
        if(StringUtils.isEmpty(jwtToken)) return "";
        Jws<Claims> claimsJws = Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
        Claims claims = claimsJws.getBody();
        return (String)claims.get("id");
    }
}

需要注意这里

九、编写代码

controller层

package com.sise.wangzhan.controller;


import com.sise.wangzhan.entity.Member;
import com.sise.wangzhan.service.MemberService;
import com.sise.wangzhan.utils.JwtUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RestController;

/**
 * <p>
 *  前端控制器
 * </p>
 *
 * @author wangzhan
 * @since 2021-01-11
 */
@RestController
@RequestMapping("/wangzhan/member")
public class MemberController {

    @Autowired
    private MemberService memberService;

    @PostMapping(value = "addMember")
    public String addMember(@RequestBody Member member){
        // 判空
        if (member == null || StringUtils.isEmpty(member.getNickname()) ||
                StringUtils.isEmpty(member.getPassword()) ||
                StringUtils.isEmpty(member.getMobile())){
            return "传过来的参数为空";
        }

        int count = memberService.saveMember(member);

        if (count == 1){
            return "添加成功";
        }else {
            return "添加失败";
        }
    }

    @PostMapping(value = "/login")
    public String login(@RequestBody Member member){

        // 判空
        if (member == null || StringUtils.isEmpty(member.getNickname()) ||
                StringUtils.isEmpty(member.getPassword())){
            return "传过来的用户名或密码为空";
        }

        // 根据用户名、密码查询数据
        Member loginMember = memberService.getMemberByNicknameAndPassword(member);

        System.out.println("查到的数据::::::" + loginMember);

        if (loginMember == null){
            return "用户名或密码错误";
        }else if (loginMember != null){

            System.out.println("=========" + String.valueOf(member.getId()));
            // 生成token
            String jwtToken = JwtUtils.getJwtToken(String.valueOf(member.getId()), member.getNickname());

            return jwtToken;
        }

        return null;
    }
}

2、service层

package com.sise.wangzhan.service;

import com.sise.wangzhan.entity.Member;
import com.baomidou.mybatisplus.extension.service.IService;

/**
 * <p>
 *  服务类
 * </p>
 *
 * @author wangzhan
 * @since 2021-01-11
 */
public interface MemberService extends IService<Member> {

    Member getMemberByNicknameAndPassword(Member member);

    int saveMember(Member member);
}

package com.sise.wangzhan.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.sise.wangzhan.entity.Member;
import com.sise.wangzhan.mapper.MemberMapper;
import com.sise.wangzhan.service.MemberService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author wangzhan
 * @since 2021-01-11
 */
@Service
public class MemberServiceImpl extends ServiceImpl<MemberMapper, Member> implements MemberService {

    @Override
    public Member getMemberByNicknameAndPassword(Member member) {

        QueryWrapper wrapper = new QueryWrapper();
        wrapper.eq("nickname", member.getNickname());
        // 这里就不涉及加密了,需要的,自行进行加密处理
        wrapper.eq("password", member.getPassword());

        return baseMapper.selectOne(wrapper);
    }

    @Override
    public int saveMember(Member member) {
        
        int count = baseMapper.insert(member);

        return count;
    }
}


十、swagger测试结果

用户名为空

返回成功的token

十一、根据前端返回的token获取用户信息

对应的id类型要修改

然后再根据id这个条件查数据就行