Spring Boot +JWT +MybatisPlus,使用Token登录详细教程,附源码!

10 篇文章 0 订阅
1 篇文章 0 订阅

一、新建Spring Boot项目
1.File→New→Module
在这里插入图片描述
2.点击下一步在这里插入图片描述
3.写完这些,点击下一步在这里插入图片描述
4.选择插件
在这里插入图片描述
5.选择项目地址,选择完成后点击Finish
在这里插入图片描述

1…创建完成后,修改pom.xml文件,添加以下依赖

<!-- MybatisPlus 核心库 -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.2.0</version>
        </dependency>
        <!-- StringUtilS工具 -->
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.5</version>
        </dependency>
        <!-- JSON工具 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.45</version>
        </dependency>
        <!-- JWT依赖 -->
        <!-- https://mvnrepository.com/artifact/com.auth0/java-jwt -->
        <dependency>
            <groupId>com.auth0</groupId>
            <artifactId>java-jwt</artifactId>
            <version>3.8.3</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.1</version>
        </dependency>

2.修改配置文件application.properties名称为application.yml,并创建新文件application-dev.yml
在这里插入图片描述
3.application.yml配置

spring:
  profiles:
    active: dev

4.application-dev.yml配置

server:
  port: 9099
spring:
  application:
    name: demo
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://127.0.0.1:3306/service_data?characterEncoding=utf-8&userUnicode=true&serverTimezone=Asia/Shanghai&useSSL=false
    username: root
    password: root
    hikari:
      minimum-idle: 3
      maximum-pool-size: 5
      max-lifetime: 0
  jackson:
    time-zone: GMT+8
    date-format: yyyy-MM-dd HH:mm:ss
    default-property-inclusion: non_null
  servlet:
    multipart:
      max-file-size: 1024MB
      max-request-size: 2048MB
  #thymeleaf的配置
  thymeleaf:
    cache: false
    enabled: true
#配置mybatisplus
mybatis-plus:
  mapper-locations: classpath*:mapper/**/*.xml
  global-config:
    db-config:
      id-type: auto
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

5.在Navicat中执行sql脚本

*/

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for user_kmmy
-- ----------------------------
DROP TABLE IF EXISTS `user_kmmy`;
CREATE TABLE `user_kmmy`  (
  `userid` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '用户ID 绝对唯一',
  `email` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT '' COMMENT '用户邮箱',
  `phone` varchar(11) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT '用户手机',
  `usertype` int(10) NOT NULL DEFAULT 0 COMMENT '用户权限类型:0普通,1管理员',
  `subuser` tinyint(2) UNSIGNED NOT NULL DEFAULT 0 COMMENT '是否有子账户',
  `uname` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT '用户名',
  `passwd` char(40) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT '密码',
  `isactive` tinyint(1) UNSIGNED NOT NULL DEFAULT 0 COMMENT '是否激活',
  `salt` char(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT '加密盐值',
  `head_img` varchar(60) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '' COMMENT '用户头像',
  `locked` tinyint(2) NOT NULL DEFAULT 0 COMMENT '禁用账号 : 0 正常 , 1 禁用',
  `create_time` timestamp(0) NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '账号创建时间',
  `last_login_time` timestamp(0) NULL DEFAULT CURRENT_TIMESTAMP COMMENT '用户最后登陆时间',
  `update_time` timestamp(0) NULL DEFAULT CURRENT_TIMESTAMP COMMENT '管理员更新用户状态的时间',
  PRIMARY KEY (`userid`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 12 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '用户表' ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of user_kmmy
-- ----------------------------
INSERT INTO `user_kmmy` VALUES (10, '', '', 0, 0, '用户Root', '54725db42d08af904bb26647bc2b892f', 0, '2d793d799d5f4e3f9eafa9522f6b4fb2', '/images/defaultUserTitle.jpg', 0, '2020-06-05 13:43:52', '2020-06-05 13:43:52', '2020-06-05 13:43:52', NULL, NULL);

SET FOREIGN_KEY_CHECKS = 1;

6.修改主启动类,在主启动类上加上@MapperScan注解,后面跟自己的项目组名

@MapperScan(basePackages = {"com.kmmy.app*.mapper"})

三、
1.新建common包
包下创建工具类
在这里插入图片描述
MD5Utils


/**
 * @author zsj
 * 2019-08-20
 */
public class MD5Utils {
    /**
     * 使用md5的算法进行加密
     */
    public static String md5(String plainText) {
        byte[] secretBytes = null;
        try {
            secretBytes = MessageDigest.getInstance("md5").digest(
                    plainText.getBytes());
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("没有md5这个算法!");
        }
        // 16进制数字
        String md5code = new BigInteger(1, secretBytes).toString(16);
        // 如果生成数字未满32位,需要前面补0
        for (int i = 0; i < 32 - md5code.length(); i++) {
            md5code = "0" + md5code;
        }
        return md5code;
    }

	// fdgdfgvsdfgs5e5a3c75d4e1
    public static void main(String[] args) {
        System.out.println(md5("E10ADC3949BA59ABBE56E057F20F883E"));
    }

OutputObject

/**
 * @Description: 出参对象
 * @Author: zsj
 * @CreateDate: 2019/8/16 19:43
 * @UpdateRemark: 修改内容
 * @Version: 1.0
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class OutputObject implements Serializable {

    /**
     * 响应码
     */
    private String respCode;
    /**
     * 响应信息
     */
    private String respMessage;
    /**
     * 响应数据
     */
    private Object data;

    /**
     * toString
     *
     * @return
     */
    @Override
    public String toString() {
        return "OutputObject{" +
                "respCode='" + respCode + '\'' +
                ", respMessage='" + respMessage + '\'' +
                ", data=" + data +
                '}';
    }

ResultObj

/**
 * @Author: zsj
 * @Date: 2019/11/21 21:35
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ResultObj {

    private String respCode;
    private String respMessage;

    public static final ResultObj LOGIN_SUCCESS = new ResultObj(ReturnCode.SUCCESS, "登陆成功");
    public static final ResultObj LOGIN_FAIL_PASS = new ResultObj(ReturnCode.FAIL, "用户名或密码错误");
    public static final ResultObj LOGIN_FAIL_CODE = new ResultObj(ReturnCode.FAIL, "验证码错误");
    public static final ResultObj THE_USER_ALREADY_EXISTS = new ResultObj(ReturnCode.FAIL, "该用户已存在");

    public static final ResultObj ADD_SUCCESS = new ResultObj(ReturnCode.SUCCESS, "添加成功");
    public static final ResultObj  ADD_ERROR= new ResultObj(ReturnCode.FAIL, "添加失败");

    public static final ResultObj DELETE_SUCCESS = new ResultObj(ReturnCode.SUCCESS, "删除成功");
    public static final ResultObj DELETE_FAIL = new ResultObj(ReturnCode.FAIL, "删除失败");

    public static final ResultObj UPDATE_SUCCESS = new ResultObj(ReturnCode.SUCCESS, "修改成功");
    public static final ResultObj UPDATE_FAIL = new ResultObj(ReturnCode.FAIL, "修改失败");
    public static final ResultObj ILLEGAL_MOBILE_NUMBER = new ResultObj(ReturnCode.FAIL, "手机号不合法");

    public static final ResultObj RESET_SUCCESS = new ResultObj(ReturnCode.SUCCESS, "重置成功");
    public static final ResultObj RESET_FAIL = new ResultObj(ReturnCode.FAIL, "重置失败");

    public static final ResultObj DISPATCH_SUCCESS = new ResultObj(ReturnCode.SUCCESS, "分配成功");
    public static final ResultObj DISPATCH_FAIL = new ResultObj(ReturnCode.FAIL, "分配失败");

    public static final ResultObj BACKINPORT_SUCCESS = new ResultObj(ReturnCode.SUCCESS, "退货成功");
    public static final ResultObj BACKINPORT_FAIL = new ResultObj(ReturnCode.FAIL, "退货失败");
    public static final ResultObj SYNCCACHE_SUCCESS = new ResultObj(ReturnCode.SUCCESS, "同步缓存成功");

    public static final ResultObj DELETE_FAIL_NEWS = new ResultObj(ReturnCode.FAIL, "删除用户失败,该用户是其他用户的直属领导,请先修改该用户的下属的直属领导,再进行删除操作");
    public static final ResultObj DELETE_QUERY = new ResultObj();


}

ReturnCode

/**
 * 响应码
 *
 * @author zhushaojie
 */
public class ReturnCode {

    /**
     * 成功
     */
    public static final String SUCCESS = "200";

    /**
     * 失败
     */
    public static final String FAIL = "400";

    /**
     * 未签证
     */
    public static final String UNAUTHORIZED = "401";

    /**
     * 未找到接口
     */
    public static final String NOT_FOUND = "404";

    /**
     * 服务器内部错误
     */
    public static final String INTERNAL_SERVER_ERROR = "500";

    /**
     * 用户默认图片
     */
    public static final String DEFAULT_IMG_USER = "/images/defaultUserTitle.jpg";

}

TokenUtil

public class TokenUtil {

    private static final long EXPIRE_TIME= 10*60*60*1000;
    private static final String TOKEN_SECRET="txdy";  //密钥盐

    /**
     * 签名生成
     * @param user
     * @return
     */
    public static String sign(UserKmmy user){
        String token = null;
        try {
            Date expiresAt = new Date(System.currentTimeMillis() + EXPIRE_TIME);
            token = JWT.create()
                    .withIssuer("auth0")
                    .withClaim("username", user.getUname())
                    .withExpiresAt(expiresAt)
                    // 使用了HMAC256加密算法。
                    .sign(Algorithm.HMAC256(TOKEN_SECRET));
        } catch (Exception e){
            e.printStackTrace();
        }
        return token;
    }

    /**
     * 签名验证
     * @param token
     * @return
     */
    public static boolean verify(String token){
        try {
            JWTVerifier verifier = JWT.require(Algorithm.HMAC256(TOKEN_SECRET)).withIssuer("auth0").build();
            DecodedJWT jwt = verifier.verify(token);
            System.out.println("认证通过:");
            System.out.println("username: " + jwt.getClaim("username").asString());
            System.out.println("过期时间:      " + jwt.getExpiresAt());
            return true;
        } catch (Exception e){
            return false;
        }
    }
}

UUIDUtils


/**
 * @author zsj
 * @version 1.0
 * @className com.deyi.govaffair.util.UUIDUtils
 * @date: 2019/8/20 10:36
 * @description TODO
 */
public class UUIDUtils {

    public static String getUUID() {
        return UUID.randomUUID().toString().replace("-","");
    }

    public static void main(String[] args) {
        System.out.println(getUUID());
    }
}

四、新建config包
创建MybatisPlusConfig类

/**
 * @Author: zhushaojie
 * @Date: 2019/11/23 19:16
 */
@Configuration
@ConditionalOnClass(value = {PaginationInterceptor.class})
public class MybatisPlusConfig {
    private final Logger logger = LoggerFactory.getLogger(this.getClass());
    @Bean
    public PaginationInterceptor paginationInterceptor() {
        logger.info("====Mybatis Plus====");
        return new PaginationInterceptor();
    }
}

WebConfiguration

/**
 * 跨域请求支持/token拦截
 * tip:只能写在一个配置类里
 */
@Configuration
public class WebConfiguration implements WebMvcConfigurer {

    private TokenInterceptor tokenInterceptor;

    private final Logger logger = LoggerFactory.getLogger(this.getClass());
    //构造方法
    public WebConfiguration(TokenInterceptor tokenInterceptor){
        this.tokenInterceptor = tokenInterceptor;
    }

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowCredentials(true)
                .allowedHeaders("*")
                .maxAge(3600)
                .allowedMethods("GET","POST","DELETE","PUT")
                .allowedOrigins("*");
        logger.info("====解决跨域问题成功!!!====");

    }

    @Override
    public void configureAsyncSupport(AsyncSupportConfigurer configurer){
        configurer.setTaskExecutor(new ConcurrentTaskExecutor(Executors.newFixedThreadPool(3)));
        configurer.setDefaultTimeout(30000);
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry){
        List<String> excludePath = new ArrayList<>();
        // 排除拦截
        excludePath.add("/user/**");  //登录
        excludePath.add("/webjars/**");
        excludePath.add("/static/**");  //静态资源
        excludePath.add("/assets/**");  //静态资源
        logger.info("====通过登录拦截器====");
        registry.addInterceptor(tokenInterceptor)
                .addPathPatterns("/**")
                .excludePathPatterns(excludePath);
        WebMvcConfigurer.super.addInterceptors(registry);
    }
}

五、新建interceptor包
创建WebConfiguration类

@Component
public class TokenInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception{
        if(request.getMethod().equals("OPTIONS")){
            response.setStatus(HttpServletResponse.SC_OK);
            return true;
        }
        response.setCharacterEncoding("utf-8");
        String token = request.getHeader("token");
        if(token != null){
            boolean result = TokenUtil.verify(token);
            if(result){
                System.out.println("通过拦截器");
                return true;
            }
        }
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json; charset=utf-8");
        try{
            JSONObject json = new JSONObject();
            json.put("msg","token verify fail");
            json.put("code","50000");
            response.getWriter().append(json.toJSONString());
            System.out.println("认证失败,未通过拦截器");
        }catch (Exception e){
            e.printStackTrace();
            response.sendError(500);
            return false;
        }
        return false;
    }
}

六、新建pojo包
创建UserKmmy实体类

/**
 * <p>
 * 用户表
 * </p>
 *
 * @author 朱少杰
 * @since 2020-06-04
 */
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
public class UserKmmy implements Serializable {

    private static final long serialVersionUID=1L;

    /**
     * 用户ID 绝对唯一
     */
    @TableId(value = "userid", type = IdType.AUTO)
    private Long userid;

    /**
     * 用户邮箱
     */
    private String email;

    /**
     * 用户手机
     */
    private String phone;

    /**
     * 用户权限类型:0普通,1管理员
     */
    private Integer usertype;

    /**
     * 是否有子账户
     */
    private Integer subuser;

    /**
     * 用户名
     */
    private String uname;

    /**
     * 密码
     */
    private String passwd;

    /**
     * 是否激活
     */
    private Boolean isactive;

    /**
     * 加密盐值
     */
    private String salt;

    /**
     * 用户头像
     */
    private String headImg;

    /**
     * 禁用账号 : 0 正常 , 1 禁用
     */
    private Integer locked;

    /**
     * 账号创建时间
     */
    private Date createTime;

    /**
     * 用户最后登陆时间
     */
    private Date lastLoginTime;

    /**
     * 管理员更新用户状态的时间
     */
    private Date updateTime;


    public UserKmmy(String uname, String passwd) {
        this.uname = uname;
        this.passwd = passwd;
    }
}

七、创建controller包
创建UserController类

/**
 * <p>
 * 用户表 前端控制器
 * </p>
 *
 * @author 朱少杰
 * @since 2020-06-04
 */
@RestController
@RequestMapping("/user")
public class UserController {

    @Autowired
    private UserService userService;

    @PostMapping("login")
    @ResponseBody
    public OutputObject login(@RequestBody UserKmmy user){
        QueryWrapper<UserKmmy> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq(user.getUname() != null, "uname", user.getUname());
        UserKmmy users = userService.getOne(queryWrapper);
        // 密码校验
        String s = (MD5Utils.md5(user.getPasswd()+users.getSalt()));
        if (users.getPasswd().equals(s)==false){
            return new OutputObject(ReturnCode.FAIL,"密码不正确",user);
        }
        queryWrapper.in(user.getPasswd() != null, "passwd", s);
        // 通过用户名从数据库中查询出该用户
        if (users == null){
            return new OutputObject(ReturnCode.FAIL,"用户不存在",user);
        }
        String token = TokenUtil.sign(new UserKmmy(user.getUname(),s));
        HashMap<String,Object> hs =new HashMap<>();
        hs.put("token",token);
        hs.put("userid",users.getUserid());
        return new OutputObject(String.valueOf(HttpStatus.OK.value()),"成功",hs);

    }

    /**
     * 用户注册
     * @param user
     * @return
     */
    @PostMapping("addUser")
    @ResponseBody
    public ResultObj addUser(@RequestBody UserKmmy user) {
        try {
            // 查询用户名是否存在
            QueryWrapper<UserKmmy> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("uname",user.getUname());
            UserKmmy users = userService.getOne(queryWrapper);
            if (users!=null){
                return ResultObj.THE_USER_ALREADY_EXISTS;
            }
            // 设置盐
            String salt = UUIDUtils.getUUID();
            user.setSalt(salt);
            // 设置密码加密
            String s = MD5Utils.md5(user.getPasswd()+salt);
            // 设置用户默认头像
            user.setPasswd(s);
            user.setHeadImg(ReturnCode.DEFAULT_IMG_USER);
            userService.save(user);
            return ResultObj.ADD_SUCCESS;
        } catch (Exception e) {
            e.printStackTrace();
            return ResultObj.ADD_ERROR;
        }
    }
    @RequestMapping("/index")
    public String index() {
        return "Hello World! 欢迎来到 spring boot application";
    }

}

八、创建service包
1.新建UserService接口类

/**
 * <p>
 * 用户表 服务类
 * </p>
 *
 * @author 朱少杰
 * @since 2020-06-04
 */
public interface UserService extends IService<UserKmmy> {

}

2.在service层下创建impl包
并创建UserServiceImpl类

/**
 * <p>
 * 用户表 服务实现类
 * </p>
 *
 * @author 朱少杰
 * @since 2020-06-04
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, UserKmmy> implements UserService {

}

九、创建mapper包
包下新建UserMapper类

public interface UserMapper extends BaseMapper<UserKmmy> {

}

十、在resources下新建mapper,在这个下面新建是为了方便idea编译
在mapper下新建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.kmmy.app.mapper.UserMapper">

</mapper>

到这里登录项目就完成了,启动项目
启动成功截图
在这里插入图片描述
测试登录,我在这里使用的postman接口测试工具进行测试

{
"uname":"用户Root",
"passwd":"1111"
}

在这里插入图片描述
到这里整个项目就结束了,有遇到问题的小伙伴可以下方评论或者关注微信公众号联系我,希望能帮助到你们!
在这里插入图片描述

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值