mirror of
https://gitee.com/youlaitech/youlai-mall.git
synced 2025-01-04 01:52:21 +08:00
feat:微信小程序登录认证
This commit is contained in:
parent
05399119b9
commit
2091712057
@ -107,6 +107,12 @@
|
||||
<artifactId>common-rabbitmq</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.youlai</groupId>
|
||||
<artifactId>common-log</artifactId>
|
||||
<version>${youlai.version}</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
@ -41,7 +41,7 @@ public class RabbitMQConfig {
|
||||
// 延时队列的消息过期了,会自动触发消息的转发,根据routingKey发送到指定的exchange中,exchange路由到死信队列
|
||||
Map<String, Object> args = new HashMap<>();
|
||||
args.put("x-dead-letter-exchange", "order.exchange");
|
||||
args.put("x-dead-letter-routing-key", "order:close"); // 死信路由Key
|
||||
args.put("x-dead-letter-routing-key", "order.close"); // 死信路由Key
|
||||
args.put("x-message-ttl", 60000); // 单位:毫秒,1分钟测试使用
|
||||
return new Queue("order.delay.queue", true, false, false, args);
|
||||
}
|
||||
@ -66,11 +66,11 @@ public class RabbitMQConfig {
|
||||
|
||||
/**
|
||||
* 死信队列绑定交换机
|
||||
* 其中死信路由的routingKey=order:close和延时队列的routingKey一致,延时队列过期时将消息发送给exchange,exchange再路由到死信队列
|
||||
* 其中死信路由的routingKey=order.close和延时队列的routingKey一致,延时队列过期时将消息发送给exchange,exchange再路由到死信队列
|
||||
*/
|
||||
@Bean
|
||||
public Binding closeOrderQueueBinding() {
|
||||
return new Binding("order.close.queue", Binding.DestinationType.QUEUE,"order.exchange","order:close",null);
|
||||
return new Binding("order.close.queue", Binding.DestinationType.QUEUE,"order.exchange","order.close",null);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -30,7 +30,7 @@ import java.util.Optional;
|
||||
* @date 2020-12-30 22:31:10
|
||||
*/
|
||||
@Api(tags = "【系统管理】订单服务")
|
||||
@RestController("V1-OrderController")
|
||||
@RestController("AdminOrderController")
|
||||
@RequestMapping("/api/v1/orders")
|
||||
@Slf4j
|
||||
@AllArgsConstructor
|
||||
|
@ -30,7 +30,7 @@ public interface SkuFeignClient {
|
||||
Result<Boolean> unlockStock(@RequestParam String orderToken);
|
||||
|
||||
|
||||
@PutMapping("/v2/skus/deduct_stock")
|
||||
@PutMapping("/app-api/v1/skus/deduct_stock")
|
||||
Result deductStock(@RequestParam String orderToken);
|
||||
|
||||
|
||||
|
@ -102,7 +102,8 @@
|
||||
|
||||
<dependency>
|
||||
<groupId>com.youlai</groupId>
|
||||
<artifactId>common-es</artifactId>
|
||||
<artifactId>common-log</artifactId>
|
||||
<version>${youlai.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
|
@ -1,6 +1,5 @@
|
||||
package com.youlai.mall.pms.controller.app;
|
||||
|
||||
import com.youlai.common.elasticsearch.service.ElasticSearchService;
|
||||
import com.youlai.common.result.Result;
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiImplicitParam;
|
||||
@ -21,8 +20,6 @@ import org.springframework.web.bind.annotation.*;
|
||||
@AllArgsConstructor
|
||||
public class SearchController {
|
||||
|
||||
private ElasticSearchService elasticSearchService;
|
||||
|
||||
@ApiOperation(value = "关键字搜索商品")
|
||||
@ApiImplicitParams({
|
||||
@ApiImplicitParam(name = "key", value = "关键字", paramType = "query", dataType = "String"),
|
||||
|
@ -41,15 +41,15 @@ public class SmsSeckillSession implements Serializable {
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
private Date gmtCreate;
|
||||
private Date createTime;
|
||||
|
||||
/**
|
||||
* 修改时间
|
||||
*/
|
||||
private Date gmtModified;
|
||||
private Date updateTime;
|
||||
|
||||
@TableField(exist = false)
|
||||
private List<SmsSeckillSkuRelation> relations;
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
}
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ import java.util.List;
|
||||
|
||||
@Api(tags = "【移动端】营销广告")
|
||||
@RestController("APPAdvertController")
|
||||
@RequestMapping("/api-app/v1/adverts")
|
||||
@RequestMapping("/app-api/v1/adverts")
|
||||
@Slf4j
|
||||
@AllArgsConstructor
|
||||
public class AdvertController {
|
||||
|
@ -15,11 +15,11 @@ public interface MemberAddressFeignClient {
|
||||
/**
|
||||
* 获取地址详情
|
||||
*/
|
||||
@GetMapping("/v1/addresses/{id}")
|
||||
@GetMapping("/app-api/v1/addresses/{id}")
|
||||
Result<UmsAddress> getById(@PathVariable("id") Long id);
|
||||
|
||||
|
||||
@GetMapping("/v1/addresses")
|
||||
@GetMapping("/app-api/v1/addresses")
|
||||
Result<List<UmsAddress>> list(@RequestParam Long memberId);
|
||||
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ import org.springframework.web.bind.annotation.*;
|
||||
public interface MemberFeignClient {
|
||||
|
||||
@PostMapping("/app-api/v1/members")
|
||||
Result<UmsMember> add(@RequestBody UmsMember member);
|
||||
Result<Long > add(@RequestBody UmsMember member);
|
||||
|
||||
|
||||
@PutMapping("/app-api/v1/members/{id}")
|
||||
|
@ -14,4 +14,5 @@ public class MemberDTO {
|
||||
private String mobile;
|
||||
|
||||
private Long balance;
|
||||
|
||||
}
|
||||
|
@ -61,10 +61,10 @@ public class MemberController {
|
||||
@ApiOperation(value = "新增会员")
|
||||
@ApiImplicitParam(name = "member", value = "实体JSON对象", required = true, paramType = "body", dataType = "UmsMember")
|
||||
@PostMapping
|
||||
public Result<UmsMember> add(@RequestBody UmsMember member) {
|
||||
public Result<Long> add(@RequestBody UmsMember member) {
|
||||
boolean status = iUmsMemberService.save(member);
|
||||
if (status) {
|
||||
return Result.success(member);
|
||||
return Result.success(member.getId());
|
||||
} else {
|
||||
return Result.failed();
|
||||
}
|
||||
@ -78,7 +78,7 @@ public class MemberController {
|
||||
return Result.judge(status);
|
||||
}
|
||||
|
||||
@ApiOperation(value = "获取当前请求的会员信息")
|
||||
@ApiOperation(value = "获取登录会员信息")
|
||||
@GetMapping("/me")
|
||||
public Result getMemberInfo() {
|
||||
Long userId = JwtUtils.getUserId();
|
||||
|
@ -4,7 +4,9 @@ import cn.hutool.json.JSONObject;
|
||||
import com.nimbusds.jose.jwk.JWKSet;
|
||||
import com.nimbusds.jose.jwk.RSAKey;
|
||||
import com.youlai.auth.common.enums.OAuthClientEnum;
|
||||
import com.youlai.auth.service.impl.WeAppServiceImpl;
|
||||
import com.youlai.auth.domain.OAuthToken;
|
||||
import com.youlai.auth.domain.UserInfo;
|
||||
import com.youlai.auth.service.IAuthService;
|
||||
import com.youlai.common.constant.AuthConstants;
|
||||
import com.youlai.common.result.Result;
|
||||
import com.youlai.common.web.util.JwtUtils;
|
||||
@ -34,7 +36,7 @@ import java.util.concurrent.TimeUnit;
|
||||
public class OAuthController {
|
||||
|
||||
private TokenEndpoint tokenEndpoint;
|
||||
private WeAppServiceImpl weAppServiceImpl;
|
||||
private IAuthService wechatAuthService;
|
||||
private RedisTemplate redisTemplate;
|
||||
private KeyPair keyPair;
|
||||
|
||||
@ -63,8 +65,6 @@ public class OAuthController {
|
||||
String clientId = JwtUtils.getAuthClientId();
|
||||
OAuthClientEnum client = OAuthClientEnum.getByClientId(clientId);
|
||||
switch (client) {
|
||||
case WEAPP: // 微信小程序
|
||||
return Result.success(weAppServiceImpl.login(parameters));
|
||||
case TEST: // knife4j接口测试文档使用 client_id/client_secret : client/123456
|
||||
return tokenEndpoint.postAccessToken(principal, parameters).getBody();
|
||||
default:
|
||||
@ -72,6 +72,14 @@ public class OAuthController {
|
||||
}
|
||||
}
|
||||
|
||||
@ApiOperation(value = "微信登录")
|
||||
@PostMapping("/token/{code}")
|
||||
public Result wechatLogin(@PathVariable String code, @RequestBody UserInfo userInfo) {
|
||||
OAuthToken token = wechatAuthService.login(code, userInfo);
|
||||
return Result.success(token);
|
||||
}
|
||||
|
||||
|
||||
@ApiOperation(value = "注销", notes = "logout")
|
||||
@DeleteMapping("/logout")
|
||||
public Result logout() {
|
||||
|
@ -1,15 +1,37 @@
|
||||
package com.youlai.auth.domain;
|
||||
|
||||
import com.youlai.auth.common.jwt.JwtPayloadBuilder;
|
||||
import lombok.*;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 描述: [类型描述]
|
||||
* 描述: [自定义token]
|
||||
* 创建时间: 2021/6/8
|
||||
*
|
||||
* @author hxr
|
||||
* @version 1.0.0
|
||||
* @update [序号][日期YYYY-MM-DD] [更改人姓名][变更描述]
|
||||
*/
|
||||
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Builder
|
||||
@Data
|
||||
public class OAuthToken {
|
||||
|
||||
private String access_token;
|
||||
|
||||
private String token_type = "bearer";
|
||||
|
||||
public OAuthToken accessToken(String accessToken) {
|
||||
this.access_token = accessToken;
|
||||
return this;
|
||||
}
|
||||
|
||||
public OAuthToken tokenType(String tokenType) {
|
||||
this.token_type = tokenType;
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,5 +1,8 @@
|
||||
package com.youlai.auth.service;
|
||||
|
||||
import com.youlai.auth.domain.OAuthToken;
|
||||
import com.youlai.auth.domain.UserInfo;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
@ -12,5 +15,5 @@ import java.util.Map;
|
||||
*/
|
||||
public interface IAuthService {
|
||||
|
||||
Map<String,Object> login(Map<String, String> parameters);
|
||||
OAuthToken login(String code, UserInfo userInfo);
|
||||
}
|
||||
|
@ -1,102 +0,0 @@
|
||||
package com.youlai.auth.service.impl;
|
||||
|
||||
import cn.binarywang.wx.miniapp.api.WxMaService;
|
||||
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.youlai.auth.common.jwt.JwtGenerator;
|
||||
import com.youlai.auth.domain.UserInfo;
|
||||
import com.youlai.auth.service.IAuthService;
|
||||
import com.youlai.common.constant.AuthConstants;
|
||||
import com.youlai.common.result.Result;
|
||||
import com.youlai.common.result.ResultCode;
|
||||
import com.youlai.common.web.exception.BizException;
|
||||
import com.youlai.mall.ums.api.MemberFeignClient;
|
||||
import com.youlai.mall.ums.pojo.domain.UmsMember;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.SneakyThrows;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author haoxr
|
||||
* @description 微信小程序认证接口
|
||||
* @createTime 2021/5/20 23:37
|
||||
*/
|
||||
@Service
|
||||
@AllArgsConstructor
|
||||
public class WeAppServiceImpl implements IAuthService {
|
||||
|
||||
private MemberFeignClient memberFeignClient;
|
||||
private WxMaService wxMaService;
|
||||
private JwtGenerator jwtGenerator;
|
||||
|
||||
/**
|
||||
* @param parameters code=小程序授权code
|
||||
* rawData=不包括敏感信息的原始数据字符串,用于计算签名
|
||||
* signature=使用 sha1( rawData + sessionkey ) 得到字符串,用于校验用户信息,详见 用户数据的签名验证和加解密
|
||||
* @return
|
||||
*/
|
||||
@SneakyThrows
|
||||
@Override
|
||||
public Map<String, Object> login(Map<String, String> parameters) {
|
||||
Map<String, Object> resultMap = new HashMap<>();
|
||||
|
||||
String code = parameters.get("code");
|
||||
String rawData = parameters.get("rawData");
|
||||
String signature = parameters.get("signature");
|
||||
WxMaJscode2SessionResult sessionInfo = wxMaService.getUserService().getSessionInfo(code);
|
||||
String sessionKey = sessionInfo.getSessionKey();
|
||||
// 校验微信用户信息
|
||||
boolean checkResult = wxMaService.getUserService().checkUserInfo(sessionKey, rawData, signature);
|
||||
if (checkResult) {
|
||||
String openid = sessionInfo.getOpenid();
|
||||
Result<UmsMember> result = memberFeignClient.getByOpenid(openid);
|
||||
|
||||
UmsMember member = null;
|
||||
Result memberOptResult = null;
|
||||
if (ResultCode.USER_NOT_EXIST.getCode().equals(result.getCode())) {
|
||||
// 用户不存在,注册成为新用户
|
||||
UserInfo userInfo = JSONUtil.toBean(rawData, UserInfo.class);
|
||||
member = new UmsMember();
|
||||
BeanUtil.copyProperties(userInfo, member);
|
||||
member.setOpenid(openid);
|
||||
member.setSessionKey(sessionKey);
|
||||
memberOptResult = memberFeignClient.add(member);
|
||||
if (ResultCode.SUCCESS.getCode().equals(memberOptResult.getCode())) {
|
||||
member = (UmsMember) memberOptResult.getData();
|
||||
}
|
||||
} else if (ResultCode.SUCCESS.getCode().equals(result.getCode()) && result.getData() != null) {
|
||||
member = result.getData();
|
||||
UserInfo userInfo = JSONUtil.toBean(rawData, UserInfo.class);
|
||||
BeanUtil.copyProperties(userInfo, member);
|
||||
member.setSessionKey(sessionKey);
|
||||
memberOptResult = memberFeignClient.update(member.getId(), member);
|
||||
}
|
||||
if (memberOptResult != null && ResultCode.SUCCESS.getCode().equals(memberOptResult.getCode())) {
|
||||
|
||||
// JWT授权,一般存放用户的角色标识,用于资源服务器(网关)鉴权
|
||||
Set<String> authorities = new HashSet<>();
|
||||
|
||||
// JWT增强,携带用户ID等信息
|
||||
Map<String, String> additional = new HashMap<>();
|
||||
additional.put(AuthConstants.USER_ID_KEY, Convert.toStr(member.getId()));
|
||||
|
||||
String accessToken = jwtGenerator.createAccessToken(authorities, additional);
|
||||
String tokenType = "bearer";
|
||||
|
||||
resultMap.put("access_token", accessToken);
|
||||
resultMap.put("token_type", tokenType);
|
||||
return resultMap;
|
||||
}
|
||||
} else {
|
||||
throw new BizException("非法用户");
|
||||
}
|
||||
throw new BizException("认证失败");
|
||||
}
|
||||
}
|
@ -0,0 +1,70 @@
|
||||
package com.youlai.auth.service.impl;
|
||||
|
||||
import cn.binarywang.wx.miniapp.api.WxMaService;
|
||||
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.convert.Convert;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import com.youlai.auth.common.jwt.JwtGenerator;
|
||||
import com.youlai.auth.domain.OAuthToken;
|
||||
import com.youlai.auth.domain.UserInfo;
|
||||
import com.youlai.auth.service.IAuthService;
|
||||
import com.youlai.common.constant.AuthConstants;
|
||||
import com.youlai.common.result.Result;
|
||||
import com.youlai.common.result.ResultCode;
|
||||
import com.youlai.mall.ums.api.MemberFeignClient;
|
||||
import com.youlai.mall.ums.pojo.domain.UmsMember;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.SneakyThrows;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author haoxr
|
||||
* @description 微信小程序认证接口
|
||||
* @createTime 2021/5/20 23:37
|
||||
*/
|
||||
@Service
|
||||
@AllArgsConstructor
|
||||
public class WechatAuthServiceImpl implements IAuthService {
|
||||
|
||||
private MemberFeignClient memberFeignClient;
|
||||
private WxMaService wxMaService;
|
||||
private JwtGenerator jwtGenerator;
|
||||
|
||||
|
||||
@SneakyThrows
|
||||
@Override
|
||||
public OAuthToken login(String code, UserInfo userInfo) {
|
||||
WxMaJscode2SessionResult sessionInfo = wxMaService.getUserService().getSessionInfo(code);
|
||||
String openid = sessionInfo.getOpenid();
|
||||
Result<UmsMember> result = memberFeignClient.getByOpenid(openid);
|
||||
UmsMember member;
|
||||
if (ResultCode.USER_NOT_EXIST.getCode().equals(result.getCode())) {
|
||||
// 用户不存在,注册成为新用户
|
||||
member = new UmsMember();
|
||||
BeanUtil.copyProperties(userInfo, member);
|
||||
member.setOpenid(openid);
|
||||
Result<Long> addRes = memberFeignClient.add(member);
|
||||
Assert.isTrue(ResultCode.SUCCESS.getCode().equals(addRes.getCode()), "微信用户注册失败");
|
||||
member.setId(addRes.getData()); // 新增后有了会员ID
|
||||
} else {
|
||||
member = result.getData();
|
||||
}
|
||||
|
||||
// 自定义JWT生成
|
||||
// 1. JWT授权,一般存放用户的角色标识,用于资源服务器(网关)鉴权
|
||||
Set<String> authorities = new HashSet<>();
|
||||
// 2. JWT增强,携带用户ID等信息
|
||||
Map<String, String> additional = new HashMap<>();
|
||||
additional.put(AuthConstants.USER_ID_KEY, Convert.toStr(member.getId()));
|
||||
String accessToken = jwtGenerator.createAccessToken(authorities, additional);
|
||||
|
||||
OAuthToken token = new OAuthToken().accessToken(accessToken);
|
||||
return token;
|
||||
}
|
||||
}
|
@ -40,7 +40,8 @@ public class AuthorizationManager implements ReactiveAuthorizationManager<Author
|
||||
|
||||
ServerHttpRequest request = authorizationContext.getExchange().getRequest();
|
||||
// Restful接口权限设计 @link https://www.cnblogs.com/haoxianrui/p/14396990.html
|
||||
String restPath = request.getMethodValue() + "_" + request.getURI().getPath();
|
||||
String restfulPath = request.getMethodValue() + "_" + request.getURI().getPath();
|
||||
log.info("restful path:{}",restfulPath);
|
||||
PathMatcher pathMatcher = new AntPathMatcher();
|
||||
// 对应跨域的预检请求直接放行
|
||||
if (request.getMethod() == HttpMethod.OPTIONS) {
|
||||
@ -52,7 +53,7 @@ public class AuthorizationManager implements ReactiveAuthorizationManager<Author
|
||||
boolean needCheck = false; // 是否被设置需要鉴权
|
||||
for (Map.Entry<String, Object> permRoles : permRolesRule.entrySet()) {
|
||||
String perm = permRoles.getKey(); // URL权限标识
|
||||
if (pathMatcher.match(perm, restPath)) {
|
||||
if (pathMatcher.match(perm, restfulPath)) {
|
||||
List<String> roles = Convert.toList(String.class, permRoles.getValue());
|
||||
hasPermRoles.addAll(Convert.toList(String.class, roles));
|
||||
needCheck = true;
|
||||
|
Loading…
Reference in New Issue
Block a user