1. 开通阿里云短信服务
2. 获取添加签名管理与模板管理
3. 获取用户AccessKey
4. 搭建service-msm模块
(1)修改pom.xml
<dependencies> <dependency> <groupId>com.aliyun</groupId> <artifactId>aliyun-java-sdk-core</artifactId> </dependency> </dependencies>
(2)添加配置文件application.properties
server.port=8204
# 服务名
spring.application.name=service-msm
#返回json的全局时间格式
spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
spring.jackson.time-zone=GMT+8
spring.redis.host=192.168.146.129
spring.redis.password=123456
spring.redis.port=6379
spring.redis.database= 0
spring.redis.timeout=1800000
# nacos服务地址
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
aliyun.sms.regionId=default
aliyun.sms.accessKeyId=xx //刚才申请获取的用户AccessKey
aliyun.sms.secret=xx
(3)配置网关
#设置路由id
spring.cloud.gateway.routes[3].id=service-msm
#设置路由的uri
spring.cloud.gateway.routes[3].uri=lb://service-msm
#设置路由断言,代理servicerId为auth-service的/auth/路径
spring.cloud.gateway.routes[3].predicates= Path=/*/msm/**
(4)封装service接口和实现类
发送短信验证码,验证码是由自己程序中生成的,然后使用阿里云服务进行发送到指定的手机上
@Service public class MsmServiceImpl implements MsmService { @Override public Boolean send(String phone, String code) { //判读手机号是否为空 if (StringUtils.isEmpty(phone)){ return false; } // 整合阿里云短信服务 //设置相关参数 DefaultProfile profile = DefaultProfile. getProfile(ConstantPropertiesUtils.REGION_Id, ConstantPropertiesUtils.ACCESS_KEY_ID, ConstantPropertiesUtils.SECRECT); IAcsClient client = new DefaultAcsClient(profile); CommonRequest request = new CommonRequest(); request.setMethod(MethodType.POST); request.setDomain("dysmsapi.aliyuncs.com"); request.setVersion("2017-05-25"); request.setAction("SendSms"); //手机号 request.putQueryParameter("PhoneNumbers", phone); //签名名称 request.putQueryParameter("SignName", "阿里云短信测试"); //模板code request.putQueryParameter("TemplateCode", "SMS_154950909"); //验证码 使用json格式 {"code":"123456"} Map<String,Object> param = new HashMap(); param.put("code",code); request.putQueryParameter("TemplateParam", JSONObject.toJSONString(param)); //调用方法进行短信发送 try { CommonResponse response = client.getCommonResponse(request); System.out.println(response.getData()); return response.getHttpResponse().isSuccess(); } catch (ClientException e) { e.printStackTrace(); } return false; } }
(5)封装controller接口
@RestController @RequestMapping("/api/msm") public class MsmApiController { @Autowired private MsmService msmService; @Autowired private RedisTemplate<String,String> redisTemplate; //发送手机验证码 @GetMapping("send/{phone}") public Result sendCode(@PathVariable String phone) { //1. 从redis中获取验证码,如果获取到返回ok String code = redisTemplate.opsForValue().get(phone); if (!StringUtils.isEmpty(code)){ return Result.ok(); } //2. 如果获取不到 //生成验证码,通过整合验证码服务进行发送 code = RandomUtil.getSixBitRandom(); Boolean isSend = msmService.send(phone,code); //生成验证码放到redis中,设置有效时间 if (isSend){ redisTemplate.opsForValue().set(phone,code,2, TimeUnit.MINUTES); return Result.ok(); }else { return Result.fail().message("发送短信失败"); } } }
5. 生成验证码后,就可以使用手机号和验证码登录
6. 搭建service-user模块
7. 在用户模块中封装Controller
@RestController @RequestMapping("/api/user") public class UserInfoApiController { @Autowired private UserInfoService userInfoService; @ApiOperation(value = "会员登录") @PostMapping("login") public Result login(@RequestBody LoginVo loginVo, HttpServletRequest request) { // loginVo.setIp(IpUtil.getIpAddr(request)); Map<String, Object> info = userInfoService.loginUser(loginVo); return Result.ok(info); } }
8. 封装service和实现类
@Service public class UserInfoServiceImpl extends ServiceImpl<UserInfoMapper, UserInfo> implements UserInfoService { @Autowired private RedisTemplate<String,String> redisTemplate; @Override public Map<String, Object> loginUser(LoginVo loginVo) { //1. 获取手机号和验证码,如果为空,抛出异常 String phone = loginVo.getPhone(); String code = loginVo.getCode(); //校验参数 if(StringUtils.isEmpty(phone) || StringUtils.isEmpty(code)) { throw new YyghException(ResultCodeEnum.PARAM_ERROR); } //2. 判断手机验证码和输入的验证码是否一致 String redisCode = redisTemplate.opsForValue().get(phone); if (!code.equals(redisCode)){ throw new YyghException(ResultCodeEnum.CODE_ERROR); } //绑定手机号码 UserInfo userInfo = null; if(!StringUtils.isEmpty(loginVo.getOpenid())) { userInfo = this.selectWxInfoOpenId(loginVo.getOpenid()); if(null != userInfo) { userInfo.setPhone(loginVo.getPhone()); this.updateById(userInfo); } else { throw new YyghException(ResultCodeEnum.DATA_ERROR); } } //如果userInfo为空,进行正常的手机登录过程 if (userInfo == null){ //判读是否是第一次登录:根据手机号查询数据库,如果不存在相同手机号就是第一次登录 QueryWrapper<UserInfo> queryWrapper = new QueryWrapper<>(); queryWrapper.eq("phone",phone); userInfo = baseMapper.selectOne(queryWrapper); if (userInfo == null){ userInfo = new UserInfo(); userInfo.setName(""); userInfo.setPhone(phone); userInfo.setStatus(1); baseMapper.insert(userInfo); } } if(userInfo.getStatus() == 0) { throw new YyghException(ResultCodeEnum.LOGIN_DISABLED_ERROR); } Map<String, Object> map = new HashMap<>(); String name = userInfo.getName(); if(StringUtils.isEmpty(name)) { name = userInfo.getNickName(); } if(StringUtils.isEmpty(name)) { name = userInfo.getPhone(); } map.put("name", name); String token = JwtHelper.createToken(userInfo.getId(), name); map.put("token", token); return map; } }
用户认证与网关整合
1. 所有请求都会经过服务网关,服务网关对外暴露服务,在网关进行统一用户认证;
2. 既然要在网关进行用户认证,网关得知道对哪些url进行认证,所以我们得对ur制定规则
3. Api接口异步请求的,我们采取url规则匹配,如:/api/**/auth/**,如凡是满足该规则的都必须用户认证
调整server-gateway模块
(1)在服务网关添加fillter
(2)在服务网关中判断用户登录状态,从header头信息中获取,登录时我们返回用户token,在服务网关中获取到token后,我在到redis中去查看用户id,如何用户id存 在,则token合法,否则不合法
@Component public class AuthGlobalFilter implements GlobalFilter, Ordered { private AntPathMatcher antPathMatcher = new AntPathMatcher(); @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { ServerHttpRequest request = exchange.getRequest(); String path = request.getURI().getPath(); System.out.println("==="+path); //内部服务接口,不允许外部访问 if(antPathMatcher.match("/**/inner/**", path)) { ServerHttpResponse response = exchange.getResponse(); return out(response, ResultCodeEnum.PERMISSION); } //api接口,异步请求,校验用户必须登录 if(antPathMatcher.match("/api/**/auth/**", path)) { Long userId = this.getUserId(request); if(StringUtils.isEmpty(userId)) { ServerHttpResponse response = exchange.getResponse(); return out(response, ResultCodeEnum.LOGIN_AUTH); } } return chain.filter(exchange); } @Override public int getOrder() { return 0; } /** * api接口鉴权失败返回数据 * @param response * @return */ private Mono<Void> out(ServerHttpResponse response, ResultCodeEnum resultCodeEnum) { Result result = Result.build(null, resultCodeEnum); byte[] bits = JSONObject.toJSONString(result).getBytes(StandardCharsets.UTF_8); DataBuffer buffer = response.bufferFactory().wrap(bits); //指定编码,否则在浏览器中会中文乱码 response.getHeaders().add("Content-Type", "application/json;charset=UTF-8"); return response.writeWith(Mono.just(buffer)); } /** * 获取当前登录用户id * @param request * @return */ private Long getUserId(ServerHttpRequest request) { String token = ""; List<String> tokenList = request.getHeaders().get("token"); if(null != tokenList) { token = tokenList.get(0); } if(!StringUtils.isEmpty(token)) { return JwtHelper.getUserId(token); } return null; } }