1 常见的加密方式回顾
2 登录认证jwt介绍
2.1 token认证
随着 Restful API、微服务的兴起,基于 Token 的认证现在已经越来越普遍。基于token的用户认证是一种服务端无状态的认证方式,所谓服务端无状态指的token本身包含登录用户所有的相关数据,而客户端在认证后的每次请求都会携带token,因此服务器端无需存放token数据。
当用户认证后,服务端生成一个token发给客户端,客户端可以放到 cookie 或 localStorage 等存储中,每次请求时带上 token,服务端收到token通过验证后即可确认用户身份。
2.2 什么是JWT?
我们现在了解了基于token认证的交互机制,但令牌里面究竟是什么内容?什么格式呢?市面上基于token的认证方式大都采用的是JWT(Json Web Token)。
JSON Web Token(JWT)是一个开放的行业标准(RFC 7519),它定义了一种简洁的、自包含的协议格式,用于在通信双方传递json对象,传递的信息经过数字签名可以被验证和信任。
JWT令牌结构:
JWT令牌由Header、Payload、Signature三部分组成,每部分中间使用点(.)分隔,比如:xxxxx.yyyyy.zzzzz
-
Header
头部包括令牌的类型(即JWT)及使用的哈希算法(如HMAC、SHA256或RSA)。
一个例子:
{ "alg": "HS256", "typ": "JWT" }
将上边的内容使用Base64Url编码,得到一个字符串就是JWT令牌的第一部分。
-
Payload
第二部分是负载,内容也是一个json对象,它是存放有效信息的地方,它可以存放jwt提供的现成字段,比如:iss(签发者),exp(过期时间戳), sub(面向的用户)等,也可自定义字段。 此部分不建议存放敏感信息,因为此部分可以解码还原原始内容。 一个例子:
{ "sub": "1234567890", "name": "456", "admin": true, "id": 123 }
最后将第二部分负载使用Base64Url编码,得到一个字符串就是JWT令牌的第二部分。
-
Signature
第三部分是签名,此部分用于防止jwt内容被篡改。 这个部分使用base64url将前两部分进行编码,编码后使用点(.)连接组成字符串,最后使用header中声明 签名算法进行签名。 一个例子:
HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
base64UrlEncode(header):jwt令牌的第一部分。 base64UrlEncode(payload):jwt令牌的第二部分。 secret:签名所使用的密钥。
下图中包含一个生成的jwt令牌:
2.3 生成token
需要引入jwt相关依赖
<dependency> <groupId>io.jsonwebtoken</groupId> <artifactId>jjwt</artifactId> </dependency>
工具类
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<String, Object> 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<Claims> 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; } }
测试token生成与解析
public static void main(String[] args) { String token = AppJwtUtil.getToken(1L); System.out.println(token); // 生成token try { Claims claimsBody = getClaimsBody(token); // 解析token 载荷信息 int i = verifyToken(claimsBody); // 校验是否过期 if(i<1){ // -1:有效,0:有效,1:过期,2:过期 Object id = claimsBody.get("id"); // 获取载荷中存储的用户id System.out.println("解析token成功 ==> 用户的id值 == "+ id); } } catch (Exception e) { e.printStackTrace(); System.out.println("解析token失败"); } }
3 admin端-登录实现
对应实体类
package com.heima.model.admin.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.util.Date; /** * <p> * 管理员用户信息表 * </p> * @author itheima */ @Data @TableName("ad_user") public class AdUser{ private static final long serialVersionUID = 1L; /** * 主键 */ @TableId(value = "id",type = IdType.AUTO) private Integer id; /** * 登录用户名 */ @TableField("name") private String name; /** * 登录密码 */ @TableField("password") private String password; /** * 盐 */ @TableField("salt") private String salt; /** * 昵称 */ @TableField("nickname") private String nickname; /** * 头像 */ @TableField("image") private String image; /** * 手机号 */ @TableField("phone") private String phone; /** * 状态 0 暂时不可用 1 永久不可用 9 正常可用 */ @TableField("status") private Integer status; /** * 邮箱 */ @TableField("email") private String email; /** * 最后一次登录时间 */ @TableField("login_time") private Date loginTime; /** * 创建时间 */ @TableField("created_time") private Date createdTime; }
思路分析:
-
检查用户是否存在
-
检查密码是否正确
-
检查用户状态是否有效
-
修改最近登录时间
-
颁发token
3.1 接口定义
接口地址:/login/in
请求方式:POST
请求数据类型:application/json
响应数据类型:application/json
接口描述: Admin端登录接口
请求示例:
{ "name": "", "password": "" }
请求参数:
参数名称 | 参数说明 | in | 是否必须 | 数据类型 | schema |
---|---|---|---|---|---|
dto | dto | body | true | AdUserDto | AdUserDto |
name | 用户名 | true | string | ||
password | 密码 | true | string |
响应结果:
{ "code":"状态码", "errorMessage":"提示信息", "data": { "token":"颁发访问凭证", "user": { // 登录用户信息 } } }
AdUserDTO
package com.heima.model.admin.dtos; import lombok.Data; @Data public class AdUserDTO { //用户名 private String name; //密码 private String password; }
3.2 mapper
package com.heima.admin.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper; import com.heima.model.admin.pojos.AdUser; public interface AdUserMapper extends BaseMapper<AdUser> { }
3.3 业务层代码
package com.heima.admin.service; import com.baomidou.mybatisplus.extension.service.IService; import com.heima.model.admin.dtos.AdUserDTO; import com.heima.model.admin.pojos.AdUser; import com.heima.model.common.dtos.ResponseResult; public interface AdUserService extends IService<AdUser> { /** * 登录功能 * @param DTO * @return */ ResponseResult login(AdUserDTO DTO); }
实现类:
@Service public class AdUserServiceImpl extends ServiceImpl<AdUserMapper, AdUser> implements AdUserService { /** * admin 登录 * @param dto * @return */ @Override public ResponseResult login(AdUserDTO dto) { //1 参数校验 if (StringUtils.isBlank(dto.getName()) || StringUtils.isBlank(dto.getPassword())) { CustException.cust(AppHttpCodeEnum.PARAM_INVALID,"参数错误"); } //2 根据用户名查询用户信息 AdUser adUser = getOne(Wrappers.<AdUser>lambdaQuery() .eq(AdUser::getName, dto.getName() ) ); if (adUser == null) { CustException.cust(AppHttpCodeEnum.DATA_NOT_EXIST,"用户名或密码错误"); } if(9 != adUser.getStatus().intValue()){ CustException.cust(AppHttpCodeEnum.LOGIN_STATUS_ERROR,"用户状态异常,请联系管理员"); } //3 获取数据库密码和盐, 匹配密码 String dbPwd = adUser.getPassword(); // 数据库密码(加密) String salt = adUser.getSalt(); // 用户输入密码(加密后) String newPwd = DigestUtils.md5DigestAsHex((dto.getPassword() + salt).getBytes()); if (!dbPwd.equals(newPwd)) { CustException.cust(AppHttpCodeEnum.LOGIN_PASSWORD_ERROR,"用户名或密码错误"); } //4 修改登录时间 adUser.setLoginTime(new Date()); updateById(adUser); //5 颁发token jwt 令牌 String token = AppJwtUtil.getToken(adUser.getId().longValue()); // 用户信息返回 VO AdUserVO userVO = new AdUserVO(); BeanUtils.copyProperties(adUser, userVO); //6 返回结果(jwt) Map map = new HashMap(); map.put("token", token); map.put("user", userVO); return ResponseResult.okResult(map); } }
封装vo对象,返回登录用户信息
package com.heima.model.admin.vo; import lombok.Data; import java.util.Date; @Data public class AdUserVO { private Integer id; private String name; private String nickname; private String image; private String email; private Date loginTime; private Date createdTime; }
3.4 控制层代码
@Api(value = "运营平台登录API",tags = "运营平台登录API") @RestController @RequestMapping("/login") public class LoginController{ @Autowired AdUserService userService; @ApiOperation("登录") @PostMapping("/in") public ResponseResult login(@RequestBody AdUserDTO DTO) { return userService.login(DTO); } }
3.5 测试
在表中创建一个用户guest,使用以下代码生成密码后修改表中的密码
String salt = "123456"; String pswd = "guest"+salt; String saltPswd = DigestUtils.md5DigestAsHex(pswd.getBytes()); System.out.println(saltPswd); //34e20b52f5bd120db806e57e27f47ed0
生成密码后的结果为:
salt:123456
password: 34e20b52f5bd120db806e57e27f47ed0
username:guest
4 网关校验jwt
4.1 全局过滤器实现jwt校验
思路分析:
-
用户进入网关开始登陆,网关过滤器进行判断,如果是登录,则路由到后台管理微服务进行登录
-
用户登录成功,后台管理微服务签发JWT TOKEN信息返回给用户
-
用户再次进入网关开始访问,网关过滤器接收用户携带的TOKEN
-
网关过滤器解析TOKEN ,判断是否有权限,如果有,则放行,如果没有则返回未认证错误
在网关微服务中新建全局过滤器:
第一步,准备工具类
把heima-leadnews-utils模块中的AppJwtUtil类拷贝到网关模块下,如下图:
第二步,编写全局过滤器
package com.heima.gateway.filter; import com.alibaba.fastjson.JSON; import com.heima.gateway.util.AppJwtUtil; import io.jsonwebtoken.Claims; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.core.annotation.Order; import org.springframework.core.io.buffer.DataBuffer; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.http.server.reactive.ServerHttpResponse; import org.springframework.stereotype.Component; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import java.util.*; /** * @Description: 认证过滤器 * @Version: V1.0 */ @Component @Slf4j @Order(0) // 值越小越优先执行 public class AuthorizeFilter implements GlobalFilter { private static List<String> urlList = new ArrayList<>(); // 初始化白名单 url路径 static { urlList.add("/login/in"); urlList.add("/v2/api-docs"); } @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { //1 判断当前是否是登录请求,如果是登录则放行 ServerHttpRequest request = exchange.getRequest(); String reqUrl = request.getURI().getPath(); for (String url : urlList) { if (reqUrl.contains(url)) { return chain.filter(exchange); } } //2 获取请求头jwt token信息 String jwtToken = request.getHeaders().getFirst("token"); if(StringUtils.isBlank(jwtToken)){ //如果不存在,向客户端返回错误提示信息 return writeMessage(exchange, "需要登录"); } //3 判断令牌信息是否正确 try { Claims claims = AppJwtUtil.getClaimsBody(jwtToken); // -1:有效,0:有效,1:过期,2:过期 int verifyToken = AppJwtUtil.verifyToken(claims); //3.1 如果不存在或失效,则拦截 if (verifyToken > 0) { return writeMessage(exchange, "认证失效,请重新登录"); } //3.2 解析JWT令牌信息 Integer id = claims.get("id", Integer.class); log.info("token网关校验成功 id:{}, URL:{}", id, request.getURI().getPath()); //***4 将令牌信息传递到对应的微服务 request.mutate().header("userId", String.valueOf(id)); //5 返回结果 return chain.filter(exchange); } catch (Exception e) { log.error("token 校验失败 :{}", e); return writeMessage(exchange, "认证失效,请重新登录"); } } /** * 返回错误提示信息 * @return */ private Mono<Void> writeMessage(ServerWebExchange exchange, String message) { Map<String, Object> map = new HashMap<>(); map.put("code", HttpStatus.UNAUTHORIZED.value()); map.put("errorMessage", message); //获取响应对象 ServerHttpResponse response = exchange.getResponse(); //设置状态码 response.setStatusCode(HttpStatus.UNAUTHORIZED); //response.setStatusCode(HttpStatus.OK); //设置返回类型 response.getHeaders().setContentType(MediaType.APPLICATION_JSON); //设置返回数据 DataBuffer buffer = response.bufferFactory().wrap(JSON.toJSONBytes(map)); //响应数据回浏览器 return response.writeWith(Flux.just(buffer)); } }
上面api中语法大家可能会感觉陌生,这是因为Gateway 采用的是基于webFlux异步非阻塞的网络处理框架,api的设计和传统SpringMVC的api大不相同, WebFlux技术比较新,目前使用还没有那么普及 所以上面代码不要求掌握, 作用理解即可。
Spring WebFlux 入门 - 废物大师兄 - 博客园
第二步,编写全局过滤器
package com.heima.gateway.filter; import com.alibaba.fastjson.JSON; import com.heima.gateway.util.AppJwtUtil; import io.jsonwebtoken.Claims; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.cloud.gateway.filter.GatewayFilterChain; import org.springframework.cloud.gateway.filter.GlobalFilter; import org.springframework.core.annotation.Order; import org.springframework.core.io.buffer.DataBuffer; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.server.reactive.ServerHttpRequest; import org.springframework.http.server.reactive.ServerHttpResponse; import org.springframework.stereotype.Component; import org.springframework.web.server.ServerWebExchange; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import java.util.*; /** * @Description: 认证过滤器 * @Version: V1.0 */ @Component @Slf4j @Order(0) // 值越小越优先执行 public class AuthorizeFilter implements GlobalFilter { private static List<String> urlList = new ArrayList<>(); // 初始化白名单 url路径 static { urlList.add("/login/in"); urlList.add("/v2/api-docs"); } @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { //1 判断当前是否是登录请求,如果是登录则放行 ServerHttpRequest request = exchange.getRequest(); String reqUrl = request.getURI().getPath(); for (String url : urlList) { if (reqUrl.contains(url)) { return chain.filter(exchange); } } //2 获取请求头jwt token信息 String jwtToken = request.getHeaders().getFirst("token"); if(StringUtils.isBlank(jwtToken)){ //如果不存在,向客户端返回错误提示信息 return writeMessage(exchange, "需要登录"); } //3 判断令牌信息是否正确 try { Claims claims = AppJwtUtil.getClaimsBody(jwtToken); // -1:有效,0:有效,1:过期,2:过期 int verifyToken = AppJwtUtil.verifyToken(claims); //3.1 如果不存在或失效,则拦截 if (verifyToken > 0) { return writeMessage(exchange, "认证失效,请重新登录"); } //3.2 解析JWT令牌信息 Integer id = claims.get("id", Integer.class); log.info("token网关校验成功 id:{}, URL:{}", id, request.getURI().getPath()); //***4 将令牌信息传递到对应的微服务 request.mutate().header("userId", String.valueOf(id)); //5 返回结果 return chain.filter(exchange); } catch (Exception e) { log.error("token 校验失败 :{}", e); return writeMessage(exchange, "认证失效,请重新登录"); } } /** * 返回错误提示信息 * @return */ private Mono<Void> writeMessage(ServerWebExchange exchange, String message) { Map<String, Object> map = new HashMap<>(); map.put("code", HttpStatus.UNAUTHORIZED.value()); map.put("errorMessage", message); //获取响应对象 ServerHttpResponse response = exchange.getResponse(); //设置状态码 response.setStatusCode(HttpStatus.UNAUTHORIZED); //response.setStatusCode(HttpStatus.OK); //设置返回类型 response.getHeaders().setContentType(MediaType.APPLICATION_JSON); //设置返回数据 DataBuffer buffer = response.bufferFactory().wrap(JSON.toJSONBytes(map)); //响应数据回浏览器 return response.writeWith(Flux.just(buffer)); } }
上面api中语法大家可能会感觉陌生,这是因为Gateway 采用的是基于webFlux异步非阻塞的网络处理框架,api的设计和传统SpringMVC的api大不相同, WebFlux技术比较新,目前使用还没有那么普及 所以上面代码不要求掌握, 作用理解即可。
https://www.cnblogs.com/cjsblog/p/12580518.html
4.2 网关token校验测试
启动admin服务,通过网关访问admin,会提示需要认证才能访问,这个时候需要在heads中设置设置token才能正常访问。
5 app端用户认证列表查询
5.1 需求分析
当用户在app前端进行了认证
对应的实体类:
package com.heima.model.user.pojos; /** * <p> * APP实名认证信息表 * </p> * * @author itheima */ @Data @TableName("ap_user_realname") public class ApUserRealname implements Serializable { private static final long serialVersionUID = 1L; /** * 主键 */ @TableId(value = "id", type = IdType.AUTO) private Integer id; /** * 账号ID */ @TableField("user_id") private Integer userId; /** * 用户名称 */ @TableField("name") private String name; /** * 资源名称 */ @TableField("idno") private String idno; /** * 正面照片 */ @TableField("font_image") private String fontImage; /** * 背面照片 */ @TableField("back_image") private String backImage; /** * 手持照片 */ @TableField("hold_image") private String holdImage; /** * 活体照片 */ @TableField("live_image") private String liveImage; /** * 状态 0 创建中 1 待审核 2 审核失败 9 审核通过 */ @TableField("status") private Short status; /** * 拒绝原因 */ @TableField("reason") private String reason; /** * 创建时间 */ @TableField("created_time") private Date createdTime; /** * 提交时间 */ @TableField("submited_time") private Date submitedTime; /** * 更新时间 */ @TableField("updated_time") private Date updatedTime; }
5.2 新建user微服务
(1)heima-leadnews-services
下新建模块:user-service
-
定义包名
-
新建引导类 参考其他微服务创建
-
pom文件引入,参考其他微服务
(3)在resources下新建bootstrap.yml
spring: application: name: leadnews-user # 服务名称 profiles: active: dev # 开发环境配置 ip: 192.168.200.130 # 环境ip地址 cloud: nacos: server-addr: ${spring.profiles.ip}:8848 discovery: # 注册中心地址配置 namespace: ${spring.profiles.active} config: # 配置中心地址配置 namespace: ${spring.profiles.active} file-extension: yml # data-id 后缀 name: ${spring.application.name} # data-id名称 shared-configs: # 共享配置 - data-id: share-feign.yml # 配置文件名-Data Id group: DEFAULT_GROUP # 默认为DEFAULT_GROUP refresh: false # 是否动态刷新,默认为false
(4)在配置中心nacos中 新建 leadnews-user.yml
配置
server: port: 9002 spring: datasource: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://${spring.profiles.ip}:3306/leadnews_user?useUnicode=true&useSSL=false&characterEncoding=UTF-8&serverTimezone=UTC username: root password: root # 设置Mapper接口所对应的XML文件位置,如果你在Mapper接口中有自定义方法,需要进行该配置 mybatis-plus: mapper-locations: classpath*:mapper/*.xml # 设置别名包扫描路径,通过该属性可以给包中的类注册别名 type-aliases-package: com.heima.model.user.pojos
5.3 接口定义
接口地址:/api/v1/auth/list
请求方式:POST
请求数据类型:application/json
响应数据类型:*/*
接口描述:
请求示例:
{ "id": 0, "msg": "", "page": 0, "size": 0, "status": 0 }
请求参数:
参数名称 | 参数说明 | in | 是否必须 | 数据类型 | schema |
---|---|---|---|---|---|
AuthDto | dto | body | true | AuthDto | AuthDto |
page | 当前第几页 | false | integer(int32) | ||
size | 每页显示条数 | false | integer(int32) | ||
status | 用户状态 | false | integer(int32) |
AuthDTO
package com.heima.model.user.dtos; import com.heima.model.common.dtos.PageRequestDTO; import lombok.Data; @Data public class AuthDTO extends PageRequestDTO { //状态 private Short status; // 认证用户ID private Integer id; //驳回的信息 private String msg; }
5.4 mapper
在user微服务下新建mapper接口:com.heima.user.mapper.ApUserRealnameMapper
package com.heima.user.mapper; public interface ApUserRealnameMapper extends BaseMapper<ApUserRealname> { }
5.5 业务层
新建业务层接口:com.heima.user.service.ApUserRealnameService
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.AuthDTO; import com.heima.model.user.pojos.ApUserRealname; public interface ApUserRealnameService extends IService<ApUserRealname> { /** * 根据状态查询需要认证相关的用户信息 * @param DTO * @return */ ResponseResult loadListByStatus(AuthDTO DTO); }
实现类:
package com.heima.user.service.impl; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.heima.common.exception.CustomException; import com.heima.model.common.dtos.PageResponseResult; import com.heima.model.common.dtos.ResponseResult; import com.heima.model.common.enums.AppHttpCodeEnum; import com.heima.model.user.dtos.AuthDTO; import com.heima.model.user.pojos.ApUserRealname; import com.heima.user.mapper.ApUserRealnameMapper; import com.heima.user.service.ApUserRealnameService; import org.springframework.stereotype.Service; @Service public class ApUserRealnameServiceImpl extends ServiceImpl<ApUserRealnameMapper, ApUserRealname> implements ApUserRealnameService { /** * 查询列表 * @param dto * @return */ @Override public ResponseResult loadListByStatus(AuthDTO dto) { // 1 参数检查 if (dto == null) { throw new CustomException(AppHttpCodeEnum.PARAM_INVALID); } dto.checkParam(); // 2 条件查询 Page<ApUserRealname> page = new Page<>(dto.getPage(), dto.getSize()); LambdaQueryWrapper<ApUserRealname> lambdaQueryWrapper = new LambdaQueryWrapper(); if (dto.getStatus() != null) { lambdaQueryWrapper.eq(ApUserRealname::getStatus, dto.getStatus()); } IPage<ApUserRealname> resultPage = page(page, lambdaQueryWrapper); // 3 返回结果 return new PageResponseResult(dto.getPage(), dto.getSize(), resultPage.getTotal(), resultPage.getRecords()); } }
5.6 控制层
package com.heima.user.controller.v1; import com.heima.model.common.dtos.ResponseResult; import com.heima.model.user.dtos.AuthDTO; import com.heima.user.service.ApUserRealnameService; 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; @Api(value = "app用户实名认证API",tags = "app用户实名认证API") @RestController @RequestMapping("/api/v1/auth") public class ApUserRealnameController { @Autowired private ApUserRealnameService userRealnameService; @ApiOperation("根据状态查询实名认证列表") @PostMapping("/list") public ResponseResult loadListByStatus(@RequestBody AuthDTO dto){ return userRealnameService.loadListByStatus(dto); } }
5.7 测试
http://localhost:9002/doc.html
6 app端用户认证后审核
6.1 需求分析
-
在app端的个人中心用户可以实名认证,需要材料为:姓名、身份证号、身份证正面照、身份证反面照、手持照片、活体照片(通过微笑、眨眼、张嘴、摇头、点头等组合动作,确保操作的为真实活体人脸。),当用户提交审核后就到了后端让运营管理人员进行审核
-
平台运营端查看用户认证信息,进行审核,其中审核包括了用户身份审核,需要对接公安系统校验身份证信息
-
用户通过审核后需要开通自媒体账号(该账号的用户名和密码与app一致)
-
用户通过审核后需要在article中在作者表中新建一个作者信息
6.1.1 实现步骤
三个角色(用户、自媒体、作者)对应的表分析:
-
ap_user:用户表。普通阅读文章的用户。不发表文章可以不用实名认证。
-
ap_user_realname:用户认证信息表。存放实名认证的用户信息,审核通过后可以发表文章数据。
-
wm_user:自媒体用户信息表
-
ap_author:文章作者信息表
-
admin后台点击审核按钮审核当前用户,传递ap_user_realname的id
-
user微服务接收到审核用户请求,按照资质条件审核
-
如果审核通过则保存用户认证信息数据到ap_user_realname中
-
再通过feign远程的调用wemedia微服务的新增自媒体用户接口,完成新增自媒体信息保存,(该账号的用户名和密码与app一致)
-
再通过feign远程的调用article微服务的新增文章作者的接口,完成新增文章作者信息保存
-
前端响应审核成功
6.2 自媒体服务-用户保存
6.2.1 wemedia微服务搭建
(1)heima-leadnews-services
下新建wemedia-service
模块,引导类和pom配置参考其他微服务
(2)resources下新建bootstrap.yml
spring: application: name: leadnews-wemedia # 服务名称 profiles: active: dev # 开发环境配置 ip: 192.168.200.130 # 环境ip地址 cloud: nacos: server-addr: ${spring.profiles.ip}:8848 discovery: # 注册中心地址配置 namespace: ${spring.profiles.active} config: # 配置中心地址配置 namespace: ${spring.profiles.active} file-extension: yml # data-id 后缀 name: ${spring.application.name} # data-id名称 shared-configs: # 共享配置 - data-id: share-feign.yml # 配置文件名-Data Id group: DEFAULT_GROUP # 默认为DEFAULT_GROUP refresh: false # 是否动态刷新,默认为false
(3)配置中心nacos创建leadnews-wemedia.yml
配置
server: port: 9004 spring: datasource: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://${spring.profiles.ip}:3306/leadnews_wemedia?useUnicode=true&useSSL=false&characterEncoding=UTF-8&serverTimezone=UTC username: root password: root # 设置Mapper接口所对应的XML文件位置,如果你在Mapper接口中有自定义方法,需要进行该配置 mybatis-plus: mapper-locations: classpath*:mapper/*.xml # 设置别名包扫描路径,通过该属性可以给包中的类注册别名 type-aliases-package: com.heima.model.media.pojos
6.2.2 自媒体用户保存和按照用户名查询
wm_user 自媒体用户表
实体类:
package com.heima.model.wemedia.pojos; /** * <p> * 自媒体用户信息表 * </p> * * @author itheima */ @Data @TableName("wm_user") public class WmUser implements Serializable { private static final long serialVersionUID = 1L; /** * 主键 */ @TableId(value = "id", type = IdType.AUTO) private Integer id; @TableField("ap_user_id") private Integer apUserId; /** * 登录用户名 */ @TableField("name") private String name; /** * 登录密码 */ @TableField("password") private String password; /** * 盐 */ @TableField("salt") private String salt; /** * 昵称 */ @TableField("nickname") private String nickname; /** * 头像 */ @TableField("image") private String image; /** * 归属地 */ @TableField("location") private String location; /** * 手机号 */ @TableField("phone") private String phone; /** * 状态 0 暂时不可用 1 永久不可用 9 正常可用 */ @TableField("status") private Integer status; /** * 邮箱 */ @TableField("email") private String email; /** * 账号类型 0 个人 1 企业 2 子账号 */ @TableField("type") private Integer type; /** * 运营评分 */ @TableField("score") private Integer score; /** * 最后一次登录时间 */ @TableField("login_time") private Date loginTime; /** * 创建时间 */ @TableField("created_time") private Date createdTime; }
(1)接口定义
接口描述: 根据用户名查询自媒体用户
接口地址:/api/v1/user/findByName/{name}
请求方式:GET
请求数据类型:*
响应数据类型:*/*
请求参数:
参数名称 | 参数说明 | in | 是否必须 | 数据类型 | schema |
---|---|---|---|---|---|
name | name | path | true | string |
接口描述: 保存自媒体用户
接口地址:/api/v1/user/save
请求方式:POST
请求数据类型:application/json
响应数据类型:*/*
请求示例:
{
"apAuthorId": 0,
"apUserId": 0,
"createdTime": "",
"email": "",
..... # 其他参数省略
}
请求参数:
参数名称 | 参数说明 | in | 是否必须 | 数据类型 | schema |
---|---|---|---|---|---|
wmUser | wmUser | body | true | WmUser | WmUser |
apAuthorId | false | integer(int32) |
(2)mapper定义
新建接口com.heima.wemedia.mapper.WmUserMapper
package com.heima.wemedia.mapper;
public interface WmUserMapper extends BaseMapper<WmUser> {
}
(3)业务层
新建接口:com.heima.wemedia.service.WmUserService
package com.heima.wemedia.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.heima.model.wemedia.pojos.WmUser;
public interface WmUserService extends IService<WmUser> {
}
实现类:
package com.heima.wemedia.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.heima.model.wemedia.pojos.WmUser;
import com.heima.wemedia.mapper.WmUserMapper;
import com.heima.wemedia.service.WmUserService;
import org.springframework.stereotype.Service;
@Service
public class WmUserServiceImpl extends ServiceImpl<WmUserMapper, WmUser> implements WmUserService {
}
(4)控制层
package com.heima.wemedia.controller.v1;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.wemedia.pojos.WmUser;
import com.heima.wemedia.service.WmUserService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
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;
/**
* @Description:
* @Version: V1.0
*/
@Api(value = "自媒体用户API",tags = "自媒体用户API")
@RestController
@RequestMapping("/api/v1/user")
public class WmUserController {
@Autowired
private WmUserService wmUserService;
@ApiOperation("保存自媒体用户信息")
@PostMapping("/save")
public ResponseResult<WmUser> save(@RequestBody WmUser wmUser) {
wmUserService.save(wmUser);
return ResponseResult.okResult(wmUser);
}
@ApiOperation("根据名称查询自媒体用户信息")
@GetMapping("/findByName/{name}")
public ResponseResult<WmUser> findByName(@PathVariable("name") String name) {
return ResponseResult.okResult(wmUserService.getOne(Wrappers.<WmUser>lambdaQuery().eq(WmUser::getName, name)));
}
}
访问http://127.0.0.1:9004/doc.html测试
6.2.3 feign远程接口定义
新建模块 heima-leadnews-feign
,主要存放所有的远程访问的接口,并且实现自动化的配置。
(1)引入依赖
<dependencies> <dependency> <groupId>com.heima</groupId> <artifactId>heima-leadnews-model</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <!--openfeign--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> </dependencies>
(2)新建包com.heima.feigns
,定义WemediaFeign
接口
package com.heima.feigns; import com.heima.config.HeimaFeignAutoConfiguration; import com.heima.feigns.fallback.WemediaFeignFallback; import com.heima.model.common.dtos.ResponseResult; import com.heima.model.wemedia.pojos.WmUser; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; @FeignClient( value = "leadnews-wemedia", fallbackFactory = WemediaFeignFallback.class, configuration = HeimaFeignAutoConfiguration.class ) public interface WemediaFeign { @PostMapping("/api/v1/user/save") public ResponseResult<WmUser> save(@RequestBody WmUser wmUser); @GetMapping("/api/v1/user/findByName/{name}") public ResponseResult<WmUser> findByName(@PathVariable("name") String name); }
(3)新建 com.heima.config
包,并创建HeimaFeignAutoConfiguration
配置类
package com.heima.config; import feign.Logger; import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; @Configuration @EnableFeignClients(basePackages = "com.heima.feigns") @ComponentScan("com.heima.feigns.fallback") public class HeimaFeignAutoConfiguration { @Bean Logger.Level level(){ return Logger.Level.FULL; } }
(4) 在com.heima.feigns.fallback
包创建 WemediaFeignFallback
服务降级类
package com.heima.feigns.fallback; import com.heima.feigns.WemediaFeign; import com.heima.model.common.dtos.ResponseResult; import com.heima.model.common.enums.AppHttpCodeEnum; import com.heima.model.wemedia.pojos.WmUser; import feign.hystrix.FallbackFactory; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; @Component @Slf4j public class WemediaFeignFallback implements FallbackFactory<WemediaFeign> { @Override public WemediaFeign create(Throwable throwable) { return new WemediaFeign() { @Override public ResponseResult<WmUser> save(WmUser wmUser) { log.error("参数: {}",wmUser); log.error("自媒体 save 远程调用出错啦 ~~~ !!!! {} ",throwable.getMessage()); return ResponseResult.errorResult(AppHttpCodeEnum.SERVER_ERROR); } @Override public ResponseResult<WmUser> findByName(String name) { log.error("参数: {}",name); log.error("自媒体 findByName 远程调用出错啦 ~~~ !!!! {} ",throwable.getMessage()); return ResponseResult.errorResult(AppHttpCodeEnum.SERVER_ERROR); } }; } }
(5)在resources目录下创建 META-INF 文件夹,并新建 spring.factories
文件
# Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ com.heima.config.HeimaFeignAutoConfiguration
(6) 在services聚合工程中引入Feign的起步依赖
<!-- 统一feign远程调用依赖 引入后可以直接注入feign接口--> <dependency> <artifactId>heima-leadnews-feign</artifactId> <groupId>com.heima</groupId> <version>1.0-SNAPSHOT</version> </dependency>
6.3 文章服务-创建作者
6.3.1 article微服务创建
(1) heima-leadnews-services
下新建模块article-service
,其中引导类和pom文件依赖参考其他微服务
(2)resources下新建bootstrap.yml
spring: application: name: leadnews-article # 服务名称 profiles: active: dev # 开发环境配置 ip: 192.168.200.130 # 环境ip地址 cloud: nacos: discovery: # 注册中心地址配置 server-addr: ${spring.profiles.ip}:8848 namespace: ${spring.profiles.active} config: # 配置中心地址配置 server-addr: ${spring.profiles.ip}:8848 namespace: ${spring.profiles.active} file-extension: yml # data-id 后缀 name: ${spring.application.name} # data-id名称 shared-configs: # 共享配置 - data-id: share-feign.yml # 配置文件名-Data Id group: DEFAULT_GROUP # 默认为DEFAULT_GROUP refresh: false # 是否动态刷新,默认为false
(3) 配置中心nacos中创建配置 leadnews-article.yml
server: port: 9003 spring: datasource: driver-class-name: com.mysql.jdbc.Driver url: jdbc:mysql://${spring.profiles.ip}:3306/leadnews_article?useUnicode=true&useSSL=false&characterEncoding=UTF-8&serverTimezone=UTC username: root password: root # 设置Mapper接口所对应的XML文件位置,如果你在Mapper接口中有自定义方法,需要进行该配置 mybatis-plus: mapper-locations: classpath*:mapper/*.xml # 设置别名包扫描路径,通过该属性可以给包中的类注册别名 type-aliases-package: com.heima.model.article.pojos
6.3.2 查询作者和保存作者
ap_author 作者信息表
(1) 接口定义
接口描述: 根据ApUser id查询作者信息
接口地址:/api/v1/author/findByUserId/{id}
请求方式:GET
请求数据类型:*
响应数据类型:*/*
请求参数:
参数名称 | 参数说明 | in | 是否必须 | 数据类型 | schema |
---|---|---|---|---|---|
id | id | path | true | integer(int32) |
响应参数:
参数名称 | 参数说明 | 类型 | schema |
---|---|---|---|
createdTime | string(date-time) | string(date-time) | |
id | integer(int32) | integer(int32) | |
name | string | ||
type | integer(int32) | integer(int32) | |
userId | integer(int32) | integer(int32) | |
wmUserId | integer(int32) | integer(int32) |
响应示例:
{
"createdTime": "",
"id": 0,
"name": "",
"type": 0,
"userId": 0,
"wmUserId": 0
}
接口描述: 保存作者信息
接口地址:/api/v1/author/save
请求方式:POST
请求数据类型:application/json
响应数据类型:*/*
请求示例:
{
"createdTime": "",
"id": 0,
"name": "",
"type": 0,
"userId": 0,
"wmUserId": 0
}
请求参数:
参数名称 | 参数说明 | in | 是否必须 | 数据类型 | schema |
---|---|---|---|---|---|
apAuthor | apAuthor | body | true | ApAuthor | ApAuthor |
createdTime | false | string(date-time) | |||
id | false | integer(int32) | |||
name | false | string | |||
type | false | integer(int32) | |||
userId | false | integer(int32) | |||
wmUserId | false | integer(int32) |
(2)mapper接口
新建mapper接口:com.heima.article.mapper.AuthorMapper
package com.heima.article.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.heima.model.article.pojos.ApAuthor;
public interface AuthorMapper extends BaseMapper<ApAuthor> {
}
(3)业务层
新建接口:com.heima.article.service.AuthorService
package com.heima.article.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.heima.model.article.pojos.ApAuthor;
public interface AuthorService extends IService<ApAuthor> {
}
实现类:
package com.heima.article.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.heima.article.mapper.AuthorMapper;
import com.heima.article.service.AuthorService;
import com.heima.model.article.pojos.ApAuthor;
import org.springframework.stereotype.Service;
@Service
public class AuthorServiceImpl extends ServiceImpl<AuthorMapper, ApAuthor> implements AuthorService {
}
(4) 控制层
新建控制器:com.heima.article.controller.AuthorController
package com.heima.article.controller.v1;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.heima.article.service.AuthorService;
import com.heima.model.article.pojos.ApAuthor;
import com.heima.model.common.dtos.ResponseResult;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@Api(value = "app作者管理API",tags = "app作者管理API")
@RestController
@RequestMapping("/api/v1/author")
public class AuthorController{
@Autowired
private AuthorService authorService;
@ApiOperation(value = "查询作者",notes = "根据appUserId查询关联作者信息")
@GetMapping("/findByUserId/{userId}")
public ResponseResult<ApAuthor> findByUserId(@PathVariable("userId") Integer userId) {
return ResponseResult.okResult(authorService.getOne(Wrappers.<ApAuthor>lambdaQuery().eq(ApAuthor::getUserId, userId)));
}
@ApiOperation(value = "保存作者",notes = "保存作者信息")
@PostMapping("/save")
public ResponseResult save(@RequestBody ApAuthor apAuthor) {
authorService.save(apAuthor);
return ResponseResult.okResult();
}
}
访问http://127.0.0.1:9003/doc.html测试
6.4 用户服务-认证审核用户
6.4.1 用户审核接口定义
接口描述: 自媒体用户审核(通过/不通过)
接口地址1:/api/v1/auth/authFail
接口地址2:/api/v1/auth/authPass
请求方式:POST
请求数据类型:application/json
响应数据类型:application/json
请求示例:
{
"id": 0,
"msg": ""
}
请求参数:
参数名称 | 参数说明 | in | 是否必须 | 数据类型 | schema |
---|---|---|---|---|---|
dto | dto | body | true | AuthDto | AuthDto |
id | false | integer(int32) | |||
msg | false | string | |||
status | false | integer(int32) |
6.4.2 用户审核mapper接口定义
在新建自媒体账户时需要把apuser信息赋值给自媒体用户
app端用户信息表
在heima-leadnews-model
中新增实体类
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; /** * <p> * APP用户信息表 * </p> * @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; }
在user-service
模块中新增mapper接口
package com.heima.user.mapper; public interface ApUserMapper extends BaseMapper<ApUser> { }
6.4.3 用户审核业务层
新建常量类:com.heima.common.constants.admin.AdminConstants
package com.heima.model.common.constants.admin; public class AdminConstants { public static final Short WAIT_AUTH = 1; public static final Short PASS_AUTH = 9; public static final Short FAIL_AUTH = 2; public static final Integer AUTHOR_TYPE = 2; // 自媒体用户 }
修改:ApUserRealnameService
新增修改状态方法
/** * 根据状态进行审核 * @param dto * @param status 2 审核失败 9 审核成功 * @return */ ResponseResult updateStatusById(AuthDTO dto, Short status);
实现类:
@Autowired ApUserMapper apUserMapper; @Autowired WemediaFeign wemediaFeign; @Autowired ArticleFeign articleFeign; @Override public ResponseResult updateStatusById(AuthDTO dto, Short status) { //1 参数检查 if (dto.getId()==null) { throw new CustomException(AppHttpCodeEnum.PARAM_INVALID); } //2 查询当前认证用户是否在 APP端有当前用户 ApUserRealname apUserRealname = getOne(Wrappers.<ApUserRealname>lambdaQuery() .eq(ApUserRealname::getId,dto.getId()) ); if (apUserRealname == null) { log.error("待审核 实名认证信息不存在 userRealnameId:{}", dto.getId()); throw new CustomException(AppHttpCodeEnum.DATA_NOT_EXIST); } if (!AdminConstants.WAIT_AUTH.equals(apUserRealname.getStatus())) { log.error("实名认证信息非待审核状态 userRealnameId:{}", dto.getId()); throw new CustomException(AppHttpCodeEnum.DATA_NOT_ALLOW); } ApUser apUser = apUserMapper.selectOne(Wrappers.<ApUser>lambdaQuery() .eq(ApUser::getId, apUserRealname.getUserId())); if(apUser == null){ log.error("实名认证信息 关联 app的用户不存在 userRealnameId:{}, userId:{} ", dto.getId(), apUserRealname.getUserId()); throw new CustomException(AppHttpCodeEnum.DATA_NOT_EXIST); } //3 更新认证用户信息 apUserRealname.setStatus(status); apUserRealname.setUpdatedTime(new Date()); if(StringUtils.isNotBlank(dto.getMsg())){ apUserRealname.setReason(dto.getMsg()); } updateById(apUserRealname); //4 认证状态如果为 通过 if (AdminConstants.PASS_AUTH.equals(status)) { //4.1 创建自媒体账户 WmUser wmUser = createWmUser(dto,apUser); //4.2 创建作者信息 createApAuthor(wmUser); } //5 返回结果 return ResponseResult.okResult(); } /** * 4.2 创建作者信息 * @param wmUser * @return */ private void createApAuthor(WmUser wmUser) { //1 检查是否成功调用 ResponseResult<ApAuthor> apAuthorResult = articleFeign.findByUserId(wmUser.getApUserId()); if(apAuthorResult.getCode().intValue() != 0){ CustException.cust(AppHttpCodeEnum.SERVER_ERROR,apAuthorResult.getErrorMessage()); } //2. 检查作者信息是否已经存在 ApAuthor apAuthor = apAuthorResult.getData(); if (apAuthor != null) { CustException.cust(AppHttpCodeEnum.DATA_EXIST,"作者信息已存在"); } //3. 添加作者信息 apAuthor = new ApAuthor(); apAuthor.setCreatedTime(new Date()); apAuthor.setName(wmUser.getName()); apAuthor.setType(AdminConstants.AUTHOR_TYPE); // 自媒体人类型 apAuthor.setUserId(wmUser.getApUserId()); // APP 用户ID apAuthor.setWmUserId(wmUser.getId()); // 自媒体用户ID ResponseResult result = articleFeign.save(apAuthor); //4. 结果失败,抛出异常 if (result.getCode() != 0) { CustException.cust(AppHttpCodeEnum.SERVER_ERROR,result.getErrorMessage()); } } /** * 4.1 创建自媒体账户 * @param dto * @param apUser APP端用户 * @return */ private WmUser createWmUser(AuthDTO dto, ApUser apUser) { //1 查询自媒体账号是否存在(APP端用户密码和自媒体密码一致) ResponseResult<WmUser> wmUserResult = wemediaFeign.findByName(apUser.getName()); if(wmUserResult.getCode().intValue() != 0){ CustException.cust(AppHttpCodeEnum.SERVER_ERROR,wmUserResult.getErrorMessage()); } WmUser wmUser =wmUserResult.getData(); if (wmUser != null) { CustException.cust(AppHttpCodeEnum.DATA_EXIST,"自媒体用户信息已存在"); } wmUser = new WmUser(); wmUser.setName(apUser.getName()); wmUser.setSalt(apUser.getSalt()); // 盐 wmUser.setPassword(apUser.getPassword()); // 密码 wmUser.setPhone(apUser.getPhone()); wmUser.setCreatedTime(new Date()); wmUser.setType(0); // 个人 wmUser.setApUserId(apUser.getId()); // app端用户id wmUser.setStatus(AdminConstants.PASS_AUTH.intValue()); ResponseResult<WmUser> saveResult = wemediaFeign.save(wmUser); if(saveResult.getCode().intValue()!=0){ CustException.cust(AppHttpCodeEnum.SERVER_ERROR,saveResult.getErrorMessage()); } return saveResult.getData(); }
6.4.4 用户审核控制层
修改ApUserRealnameController类,新增方法
@ApiOperation("实名认证通过") @PostMapping("/authPass") public ResponseResult authPass(@RequestBody AuthDTO DTO) { return userRealnameService.updateStatusById(DTO, AdminConstants.PASS_AUTH); } @ApiOperation("实名认证失败") @PostMapping("/authFail") public ResponseResult authFail(@RequestBody AuthDTO DTO) { return userRealnameService.updateStatusById(DTO, AdminConstants.FAIL_AUTH); }
6.4.5 测试
步骤:
(1)修改网关配置
在admin-gateway
模块中的application.yml
文件中新增以下配置
- id: user uri: lb://leadnews-user predicates: - Path=/user/** filters: - StripPrefix= 1