一、首先进行环境的准备
1.创建数据库
2.创建项目 依赖包如下
4.0.0
com.heima
heima-leadnews
pom
1.0-SNAPSHOT
heima-leadnews-common
heima-leadnews-utils
heima-leadnews-model
heima-leadnews-feign-api
heima-leadnews-service
heima-leadnews-gateway
heima-leadnews-test
org.springframework.boot
spring-boot-starter-parent
2.3.9.RELEASE
UTF-8
UTF-8
1.8
1.8
2.3.9.RELEASE
2.6
1.12
1.18.8
5.1.46
3.5.1
2.0.1
3.4.1
3.5
1.2.3
1.10.2
0.9.1
1.2.58
2.9.8
1.0.9.RELEASE
2.1.6
Hoxton.SR10
4.2.0
portable-1.3.4
1.26.5
4.11.1
4.12
6.3.1
7.2.0
4.1.1
3.4.1
2.6.6
2.5.1
0.9.11
2.9.2
2.0.2
2.2.5.RELEASE
2.2.0-SNAPSHOT
org.springframework.boot
spring-boot-starter-log4j2
org.projectlombok
lombok
${lombok.version}
provided
heima-leadnews-feign-api
com.heima
${project.version}
heima-leadnews-common
com.heima
${project.version}
heima-leadnews-model
com.heima
${project.version}
heima-leadnews-utils
com.heima
${project.version}
org.reflections
reflections
${reflections.version}
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-logging
org.slf4j
slf4j-log4j12
${spring.boot.version}
org.springframework.boot
spring-boot-starter
org.springframework.boot
spring-boot-starter-logging
org.slf4j
slf4j-log4j12
${spring.boot.version}
commons-io
commons-io
${commons.io.version}
org.apache.commons
commons-lang3
${lang3.version}
commons-codec
commons-codec
${commons.codec.version}
mysql
mysql-connector-java
${mysql.version}
org.mybatis
mybatis
${mybatis.version}
org.mybatis
mybatis-spring
${mybatis.spring.version}
org.jsoup
jsoup
${jsoup.version}
io.jsonwebtoken
jjwt
${jwt.version}
com.alibaba
fastjson
${fastjson.version}
com.fasterxml.jackson.dataformat
jackson-dataformat-cbor
${fastjson.dataformat.version}
com.fasterxml.jackson.dataformat
jackson-dataformat-xml
2.9.7
org.springframework.cloud
spring-cloud-dependencies
${spring.cloud.version}
pom
import
com.alibaba.cloud
spring-cloud-alibaba-dependencies
${com.alibaba.cloud}
pom
import
de.codecentric
spring-boot-admin-dependencies
${spring.boot.admin.version}
pom
import
org.apache.curator
curator-framework
${curator.version}
org.apache.curator
curator-recipes
${curator.version}
com.hankcs
hanlp
${hanlp.version}
com.github.tobato
fastdfs-client
${fastdfs.version}
com.aliyun
aliyun-java-sdk-core
${ali.core.version}
com.aliyun
aliyun-java-sdk-green
${ali.green.version}
junit
junit
${junit.version}
org.springframework.kafka
spring-kafka
${kafka.version}
org.apache.kafka
kafka-streams
${kafka.client.version}
connect-json
org.apache.kafka
org.apache.kafka
kafka-clients
${kafka.client.version}
com.github.xiaoymin
knife4j-spring-boot-starter
${knife4j.version}
io.springfox
springfox-swagger2
${swagger.version}
io.springfox
springfox-swagger-ui
${swagger.version}
com.baomidou
mybatis-plus-boot-starter
${mybatis.plus.version}
org.apache.maven.plugins
maven-compiler-plugin
3.7.0
${java.version}
${java.version}
src/main/resources
**/*.*
true
src/test/resources
true
spring
https://maven.aliyun.com/repository/spring
true
true
3. 创建一个获取token 的类
package com.heima.utils.common;
import io.jsonwebtoken.*;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.util.*;
public class AppJwtUtil {
// TOKEN的有效期一天(S)
private static final int TOKEN_TIME_OUT = 3_600;
// 加密KEY
private static final String TOKEN_ENCRY_KEY = "MDk4ZjZiY2Q0NjIxZDM3M2NhZGU0ZTgzMjYyN2I0ZjY";
// 最小刷新间隔(S)
private static final int REFRESH_TIME = 300;
// 生产ID
public static String getToken(Long id){
Map claimMaps = new HashMap();
claimMaps.put("id",id);
long currentTime = System.currentTimeMillis();
return Jwts.builder()
.setId(UUID.randomUUID().toString())
.setIssuedAt(new Date(currentTime)) //签发时间
.setSubject("system") //说明
.setIssuer("heima") //签发者信息
.setAudience("app") //接收用户
.compressWith(CompressionCodecs.GZIP) //数据压缩方式
.signWith(SignatureAlgorithm.HS512, generalKey()) //加密方式
.setExpiration(new Date(currentTime + TOKEN_TIME_OUT * 1000)) //过期时间戳
.addClaims(claimMaps) //cla信息
.compact();
}
/**
* 获取token中的claims信息
*
* @param token
* @return
*/
private static Jws getJws(String token) {
return Jwts.parser()
.setSigningKey(generalKey())
.parseClaimsJws(token);
}
/**
* 获取payload body信息
*
* @param token
* @return
*/
public static Claims getClaimsBody(String token) {
try {
return getJws(token).getBody();
}catch (ExpiredJwtException e){
return null;
}
}
/**
* 获取hearder body信息
*
* @param token
* @return
*/
public static JwsHeader getHeaderBody(String token) {
return getJws(token).getHeader();
}
/**
* 是否过期
*
* @param claims
* @return -1:有效,0:有效,1:过期,2:过期
*/
public static int verifyToken(Claims claims) {
if(claims==null){
return 1;
}
try {
claims.getExpiration()
.before(new Date());
// 需要自动刷新TOKEN
if((claims.getExpiration().getTime()-System.currentTimeMillis())>REFRESH_TIME*1000){
return -1;
}else {
return 0;
}
} catch (ExpiredJwtException ex) {
return 1;
}catch (Exception e){
return 2;
}
}
/**
* 由字符串生成加密key
*
* @return
*/
public static SecretKey generalKey() {
byte[] encodedKey = Base64.getEncoder().encode(TOKEN_ENCRY_KEY.getBytes());
SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
return key;
}
public static void main(String[] args) {
/* Map map = new HashMap();
map.put("id","11");*/
System.out.println(AppJwtUtil.getToken(1102L));
Jws jws = AppJwtUtil.getJws("eyJhbGciOiJIUzUxMiIsInppcCI6IkdaSVAifQ.H4sIAAAAAAAAADWLQQqEMAwA_5KzhURNt_qb1KZYQSi0wi6Lf9942NsMw3zh6AVW2DYmDGl2WabkZgreCaM6VXzhFBfJMcMARTqsxIG9Z888QLui3e3Tup5Pb81013KKmVzJTGo11nf9n8v4nMUaEY73DzTabjmDAAAA.4SuqQ42IGqCgBai6qd4RaVpVxTlZIWC826QA9kLvt9d-yVUw82gU47HDaSfOzgAcloZedYNNpUcd18Ne8vvjQA");
Claims claims = jws.getBody();
System.out.println(claims.get("id"));
}
}
4.nacos注册中心的配置
(1)首先业务包结构如下
(2)配置yam文件
server:
port: 51801
spring:
application:
name: leadnews-user
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
config:
server-addr: 127.0.0.1:8848
file-extension: yml
(3)进入nacos注册中心
在公共环境中新建一个配置
代码如下:
bootstrap.yml
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/leadnews_user?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
username: root
password: 875411
# 设置Mapper接口所对应的XML文件位置,如果你在Mapper接口中有自定义方法,需要进行该配置
mybatis-plus:
mapper-locations: classpath*:mapper/*.xml
# 设置别名包扫描路径,通过该属性可以给包中的类注册别名
type-aliases-package: com.heima.model.user.pojos
5.定义日志的存储信息 logbock.xml
代码如下:
%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
utf8
${LOG_HOME}/leadnews.%d{yyyy-MM-dd}.log
%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
0
512
二、业务逻辑的编写
1.创建接口 ApUserLoginController类
package com.heima.user.controller.v1;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.user.dtos.LoginDto;
import com.heima.user.service.ApUserService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
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;
@RestController
@RequestMapping("/api/v1/login")
@Api(value = "app端用户登录",tags = "app端用户登录")
public class ApUserLoginController {
@Autowired
private ApUserService apUserService;
@PostMapping("/login_auth")
@ApiOperation("用户登录")
public ResponseResult login(@RequestBody LoginDto dto){
return apUserService.login(dto);
}
}
2.根据MP,定义mapper层 ApUserMapper
package com.heima.user.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.heima.model.user.pojos.ApUser;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface ApUserMapper extends BaseMapper {
}
3.编写业务层代码
业务层接口:
package com.heima.user.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.user.dtos.LoginDto;
import com.heima.model.user.pojos.ApUser;
public interface ApUserService extends IService {
/**
* app端登录功能
* @param dto
* @return
*/
public ResponseResult login(LoginDto dto);
}
业务层实现类:
(1)首先判断是正常登录还是游客登录
使用if语句进行判断 用户名是否为空 密码是否为空
if(StringUtils.isNotBlank(dto.getPhone()) && StringUtils.isNotBlank(dto.getPassword()))
(2)如果不为空创建查询条件,查询用户名是否存在
LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper(); queryWrapper.eq(ApUser::getPhone,dto.getPhone());
(3)由MP调用mapper层的selectOne方法 根据封装条件去进行查询
ApUser dbUser = apUserMapper.selectOne(queryWrapper);
(4)如果用户名不存在(AppHttpCodeEnum类的代码在后面补充里会写到)
return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST,”用户信息不存在”);
(5)如果用户名存在继续比对密码
首先拿到盐以及密码
String salt = dbUser.getSalt(); String password = dto.getPassword();
其次调用DigestUtils.md5DigestAsHex方法进行md5加密处理
String pswd = DigestUtils.md5DigestAsHex((password + salt).getBytes());
最后进行判断 如果拿到的密码不一样返回错误信息
if(!pswd.equals(dbUser.getPassword())){ return ResponseResult.errorResult(AppHttpCodeEnum.LOGIN_PASSWORD_ERROR); }
如果拿到的密码一样获取数据拿到token
String token = AppJwtUtil.getToken(dbUser.getId().longValue());
Map map = new HashMap();
map.put(“token”,token);
//盐不返回 密码不返回 dbUser.setSalt(“”); dbUser.setPassword(“”);
map.put(“user”,dbUser);
return ResponseResult.okResult(map);
(6) 如果是游客登录
设置token为0并返回
//2.游客登录 Map map = new HashMap();
//游客登录默认token为0
map.put(“token”,AppJwtUtil.getToken(0L)); return ResponseResult.okResult(map);
业务具体代码如下:
package com.heima.user.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.user.dtos.LoginDto;
import com.heima.model.user.pojos.ApUser;
import com.heima.user.mapper.ApUserMapper;
import com.heima.user.service.ApUserService;
import com.heima.utils.common.AppJwtUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.DigestUtils;
import java.util.HashMap;
import java.util.Map;
@Service
@Transactional
@Slf4j
public class ApUserServiceImpl extends ServiceImpl implements ApUserService {
/**
* app端登录功能
* @param dto
* @return
*/
@Autowired
private ApUserMapper apUserMapper;
@Override
public ResponseResult login(LoginDto dto) {
//1.正常登录 用户名和密码
if(StringUtils.isNotBlank(dto.getPhone()) && StringUtils.isNotBlank(dto.getPassword())){
//1.1 根据手机号查询用户信息
LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper();
queryWrapper.eq(ApUser::getPhone,dto.getPhone());
ApUser dbUser = apUserMapper.selectOne(queryWrapper);
//ApUser dbUser = getOne(Wrappers.lambdaQuery().eq(ApUser::getPhone, dto.getPhone()));
if(dbUser == null){
return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST,"用户信息不存在");
}
//1.2 比对密码
//拿到盐
String salt = dbUser.getSalt();
String password = dto.getPassword();
//md5加密
String pswd = DigestUtils.md5DigestAsHex((password + salt).getBytes());
if(!pswd.equals(dbUser.getPassword())){
return ResponseResult.errorResult(AppHttpCodeEnum.LOGIN_PASSWORD_ERROR);
}
//1.3 返回数据 jwt user
//longValue())也就是将包装类中的数据拆箱成基本数据类型。
String token = AppJwtUtil.getToken(dbUser.getId().longValue());
Map map = new HashMap();
map.put("token",token);
//盐不返回 密码不返回
dbUser.setSalt("");
dbUser.setPassword("");
map.put("user",dbUser);
return ResponseResult.okResult(map);
}else {
//2.游客登录
Map map = new HashMap();
//游客登录默认token为0
map.put("token",AppJwtUtil.getToken(0L));
return ResponseResult.okResult(map);
}
}
}
三、补充类
1.启动类的代码补充
package com.heima.user; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; @SpringBootApplication @EnableDiscoveryClient @MapperScan("com.heima.user.mapper") public class UserApplication { public static void main(String[] args) { SpringApplication.run(UserApplication.class,args); } }
2.实体类的代码补充
package com.heima.model.user.pojos;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serializable;
import java.util.Date;
/**
*
* APP用户信息表
*
*
* @author itheima
*/
@Data
@TableName("ap_user")
public class ApUser implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 主键
*/
@TableId(value = "id", type = IdType.AUTO)
private Integer id;
/**
* 密码、通信等加密盐
*/
@TableField("salt")
private String salt;
/**
* 用户名
*/
@TableField("name")
private String name;
/**
* 密码,md5加密
*/
@TableField("password")
private String password;
/**
* 手机号
*/
@TableField("phone")
private String phone;
/**
* 头像
*/
@TableField("image")
private String image;
/**
* 0 男
1 女
2 未知
*/
@TableField("sex")
private Boolean sex;
/**
* 0 未
1 是
*/
@TableField("is_certification")
private Boolean certification;
/**
* 是否身份认证
*/
@TableField("is_identity_authentication")
private Boolean identityAuthentication;
/**
* 0正常
1锁定
*/
@TableField("status")
private Boolean status;
/**
* 0 普通用户
1 自媒体人
2 大V
*/
@TableField("flag")
private Short flag;
/**
* 注册时间
*/
@TableField("created_time")
private Date createdTime;
}
3.前端传入参数DTO的补充
package com.heima.model.user.dtos;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
@Data
public class LoginDto {
/**
* 手机号
*/
@ApiModelProperty(value = "手机号",required = true)
private String phone;
/**
* 密码
*/
@ApiModelProperty(value = "密码",required = true)
private String password;
}
4.状态信息枚举类的补充
package com.heima.model.common.enums;
public enum AppHttpCodeEnum {
// 成功段0
SUCCESS(200,"操作成功"),
// 登录段1~50
NEED_LOGIN(1,"需要登录后操作"),
LOGIN_PASSWORD_ERROR(2,"密码错误"),
// TOKEN50~100
TOKEN_INVALID(50,"无效的TOKEN"),
TOKEN_EXPIRE(51,"TOKEN已过期"),
TOKEN_REQUIRE(52,"TOKEN是必须的"),
// SIGN验签 100~120
SIGN_INVALID(100,"无效的SIGN"),
SIG_TIMEOUT(101,"SIGN已过期"),
// 参数错误 500~1000
PARAM_REQUIRE(500,"缺少参数"),
PARAM_INVALID(501,"无效参数"),
PARAM_IMAGE_FORMAT_ERROR(502,"图片格式有误"),
SERVER_ERROR(503,"服务器内部错误"),
// 数据错误 1000~2000
DATA_EXIST(1000,"数据已经存在"),
AP_USER_DATA_NOT_EXIST(1001,"ApUser数据不存在"),
DATA_NOT_EXIST(1002,"数据不存在"),
// 数据错误 3000~3500
NO_OPERATOR_AUTH(3000,"无权限操作"),
NEED_ADMIND(3001,"需要管理员权限");
int code;
String errorMessage;
AppHttpCodeEnum(int code, String errorMessage){
this.code = code;
this.errorMessage = errorMessage;
}
public int getCode() {
return code;
}
public String getErrorMessage() {
return errorMessage;
}
}
5.返回值的封装对象的代码补充
package com.heima.model.common.dtos;
import com.alibaba.fastjson.JSON;
import com.heima.model.common.enums.AppHttpCodeEnum;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 通用的结果返回类
* @param
*/
public class ResponseResult implements Serializable {
private String host;
private Integer code;
private String errorMessage;
private T data;
public ResponseResult() {
this.code = 200;
}
public ResponseResult(Integer code, T data) {
this.code = code;
this.data = data;
}
public ResponseResult(Integer code, String msg, T data) {
this.code = code;
this.errorMessage = msg;
this.data = data;
}
public ResponseResult(Integer code, String msg) {
this.code = code;
this.errorMessage = msg;
}
public static ResponseResult errorResult(int code, String msg) {
ResponseResult result = new ResponseResult();
return result.error(code, msg);
}
public static ResponseResult okResult(int code, String msg) {
ResponseResult result = new ResponseResult();
return result.ok(code, null, msg);
}
public static ResponseResult okResult(Object data) {
ResponseResult result = setAppHttpCodeEnum(AppHttpCodeEnum.SUCCESS, AppHttpCodeEnum.SUCCESS.getErrorMessage());
if(data!=null) {
result.setData(data);
}
return result;
}
public static ResponseResult errorResult(AppHttpCodeEnum enums){
return setAppHttpCodeEnum(enums,enums.getErrorMessage());
}
public static ResponseResult errorResult(AppHttpCodeEnum enums, String errorMessage){
return setAppHttpCodeEnum(enums,errorMessage);
}
public static ResponseResult setAppHttpCodeEnum(AppHttpCodeEnum enums){
return okResult(enums.getCode(),enums.getErrorMessage());
}
private static ResponseResult setAppHttpCodeEnum(AppHttpCodeEnum enums, String errorMessage){
return okResult(enums.getCode(),errorMessage);
}
public ResponseResult> error(Integer code, String msg) {
this.code = code;
this.errorMessage = msg;
return this;
}
public ResponseResult> ok(Integer code, T data) {
this.code = code;
this.data = data;
return this;
}
public ResponseResult> ok(Integer code, T data, String msg) {
this.code = code;
this.data = data;
this.errorMessage = msg;
return this;
}
public ResponseResult> ok(T data) {
this.data = data;
return this;
}
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public String getErrorMessage() {
return errorMessage;
}
public void setErrorMessage(String errorMessage) {
this.errorMessage = errorMessage;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
public String getHost() {
return host;
}
public void setHost(String host) {
this.host = host;
}
public static void main(String[] args) {
//前置
/*AppHttpCodeEnum success = AppHttpCodeEnum.SUCCESS;
System.out.println(success.getCode());
System.out.println(success.getErrorMessage());*/
//查询一个对象
/*Map map = new HashMap();
map.put("name","zhangsan");
map.put("age",18);
ResponseResult result = ResponseResult.okResult(map);
System.out.println(JSON.toJSONString(result));*/
//新增,修改,删除 在项目中统一返回成功即可
/* ResponseResult result = ResponseResult.errorResult(AppHttpCodeEnum.SUCCESS);
System.out.println(JSON.toJSONString(result));*/
//根据不用的业务返回不同的提示信息 比如:当前操作需要登录、参数错误
/*ResponseResult result = ResponseResult.errorResult(AppHttpCodeEnum.NEED_LOGIN);
System.out.println(JSON.toJSONString(result));*/
//查询分页信息
PageResponseResult responseResult = new PageResponseResult(1,5,50);
List list = new ArrayList();
list.add("itcast");
list.add("itheima");
responseResult.setData(list);
System.out.println(JSON.toJSONString(responseResult));
}
}
测试: