创建认证模块
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.8.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.atguigu.gulimall</groupId>
<artifactId>gulimall-auth-server</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>gulimall-auth-server</name>
<description>gulimall-auth-server</description>
<properties>
<java.version>11</java.version>
<spring-cloud.version>Greenwich.SR3</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>com.atguigu.gulimall</groupId>
<artifactId>gulimall-common</artifactId>
<version>0.0.1-SNAPSHOT</version>
<exclusions>
<exclusion>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
application.properties
spring.application.name=gulimall-auth-server
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
server.port=20000
主启动类
package com.atguigu.gulimall.auth;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
@EnableFeignClients
@EnableDiscoveryClient
@SpringBootApplication
public class GulimallAuthServerApplication {
public static void main(String[] args) {
SpringApplication.run(GulimallAuthServerApplication.class, args);
}
}
启动验证
启动服务,发现服务注册进 Nacos
页面及域名访问初始化
修改hosts实现域名访问
192.168.56.10 auth.gulimall.com
配置网关转发域名
spring:
cloud:
gateway:
routes:
- id: gulimall_auth_route
uri: lb://gulimall-auth-server
predicates:
- Host=auth.gulimall.com
引入登录页面
将登录页面和注册页面放到 templates 下,静态文件可以选择 Nginx 动静分离配置。最终目录:
认证服务-短信验证码
阿里云短信服务
https://www.aliyun.com/product/sms?spm=5176.159202.J_8058803260.68.64ae6a56APLp1H
参照文档:https://help.aliyun.com/document_detail/112148.htm?spm=a2c4g.11186623.0.0.6ccc3e0688ApNt#concept-mlr-q2b-fhb
相关配置
yaml配置
ipAddr: 192.168.56.10
spring:
application:
name: gulimall-auth-server
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
sentinel:
transport:
dashboard: localhost:8333
thymeleaf:
cache: false
session:
store-type: redis
redis:
host: ${ipAddr}
port: 6379
发送验证码组件
package com.atguigu.gulimall.thirdparty.component;
import com.aliyuncs.CommonRequest;
import com.aliyuncs.CommonResponse;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.exceptions.ServerException;
import com.aliyuncs.http.MethodType;
import com.aliyuncs.profile.DefaultProfile;
import com.atguigu.common.utils.HttpUtils;
import lombok.Data;
import org.apache.http.HttpResponse;
import org.apache.http.util.EntityUtils;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
/**
* <p>Title: SmsComponent</p>
* Description:
* date:2020/6/25 14:23
*/
@Data
@ConfigurationProperties(prefix = "spring.cloud.alicloud.sms")
@Component
public class SmsComponent {
private String accessKeyId;
private String secret;
private String signName;
private String templateCode;
/**
* 发送短信验证码
* 示例: 您的验证码是:XXXXXX,10分钟内有效。如非本人操作请注意个人信息安全。
*
* @param phone 手机号
* @param code 验证码
* @return 发送结果
*/
public String sendCode(String phone, String code) {
DefaultProfile profile = DefaultProfile.getProfile("cn-hangzhou", accessKeyId, secret);
IAcsClient client = new DefaultAcsClient(profile);
CommonRequest request = new CommonRequest();
request.setSysMethod(MethodType.POST);
request.setSysDomain("dysmsapi.aliyuncs.com");
request.setSysVersion("2017-05-25");
request.setSysProduct("Dysmsapi");
request.setSysAction("SendSms");
request.putQueryParameter("RegionId", "cn-hangzhou");
request.putQueryParameter("PhoneNumbers", phone);//接收短信的手机号码
request.putQueryParameter("SignName", signName);//短信签名名称
request.putQueryParameter("TemplateCode", templateCode);//短信模板CODE
request.putQueryParameter("TemplateParam", "{\"code\":\"" + code + "\"}");//短信模板变量对应的实际值
CommonResponse response = null;
try {
response = client.getCommonResponse(request);
System.out.println(response.getData());//{"RequestId":"51EE15A9-063F-5341-B2EC-D80E6CF0E305","Message":"OK","BizId":"167118146737237878^0","Code":"OK"}
return response.getData();
} catch (ServerException e) {
e.printStackTrace();
} catch (ClientException e) {
e.printStackTrace();
}
return "fail_" + response.getHttpStatus();
}
// public String sendSmsCode(String phone, String code){
// String method = "GET";
// Map<String, String> headers = new HashMap<String, String>();
// //最后在header中的格式(中间是英文空格)为Authorization:APPCODE 83359fd73fe94948385f570e3c139105
// headers.put("Authorization", "APPCODE " + this.appCode);
// Map<String, String> querys = new HashMap<String, String>();
// querys.put("code", code);
// querys.put("phone", phone);
// querys.put("skin", this.skin);
// querys.put("sign", this.sign);
// HttpResponse response = null;
// try {
// response = HttpUtils.doGet(this.host, this.path, method, headers, querys);
// //获取response的body
// if(response.getStatusLine().getStatusCode() == 200){
// return EntityUtils.toString(response.getEntity());
// }
// } catch (Exception e) {
// e.printStackTrace();
// }
// return "fail_" + response.getStatusLine().getStatusCode();
// }
}
测试
@Autowired
SmsComponent smsComponent;
@Test
public void testSmsComponent() {
smsComponent.sendCode("18070516157", "666666");
}
发送短信接口
@Controller
@RequestMapping("/sms")
public class SmsSendController {
@Autowired
private SmsComponent smsComponent;
/**
* 提供给别的服务进行调用的
*/
@GetMapping("/sendcode")
public R sendCode(@RequestParam("phone") String phone, @RequestParam("code") String code){
if(!"fail".equals(smsComponent.sendCode(phone, code).split("_")[0])){
return R.ok();
}
return R.error(BizCodeEnum.SMS_SEND_CODE_EXCEPTION.getCode(), BizCodeEnum.SMS_SEND_CODE_EXCEPTION.getMsg());
}
}
接口防刷和验证码再次校验
mall-auth-server LoginController.java
@ResponseBody
@GetMapping(value = "/sms/sendCode")
public R sendCode(@RequestParam("phone") String phone) {
//1、接口防刷
String redisCode = stringRedisTemplate.opsForValue().get(AuthServerConstant.SMS_CODE_CACHE_PREFIX + phone);
if (!StringUtils.isEmpty(redisCode)) {
//活动存入redis的时间,用当前时间减去存入redis的时间,判断用户手机号是否在60s内发送验证码
long currentTime = Long.parseLong(redisCode.split("_")[1]);
if (System.currentTimeMillis() - currentTime < 60000) {
//60s内不能再发
return R.error(BizCodeEnum.SMS_CODE_EXCEPTION.getCode(), BizCodeEnum.SMS_CODE_EXCEPTION.getMsg());
}
}
//2、验证码的再次效验 redis.存key-phone,value-code
int code = (int) ((Math.random() * 9 + 1) * 100000);
String codeNum = String.valueOf(code);
String redisStorage = codeNum + "_" + System.currentTimeMillis();
//存入redis,防止同一个手机号在60秒内再次发送验证码
stringRedisTemplate.opsForValue().set(AuthServerConstant.SMS_CODE_CACHE_PREFIX + phone,
redisStorage, 10, TimeUnit.MINUTES);
thirdPartFeignService.sendCode(phone, codeNum);
return R.ok();
}
认证服务-登录注册功能
参数校验补充
通过注解可以给前端传递过来的值进行校验,例如:
但是这个注解必须配合 @Valid 使用,完成对参数的校验:
而校验的结果,也会自动封装到 BindingResult 类型中,通过这个参数可以很方便的对错误的参数进行处理。
hasErrors() 可以判断是否有参数校验错误,如果有,可以通过 getFieldsErrors() 方法获取错误列表。
注意:@Valid 和 BindingResult 是一一对应的,多个 @Valid 标注的参数实体后面要对应各自的 BindingResult.
SpringMVC 重定向携带数据
MD5&MD5盐值加密
MD5
Message Digest algorithm 5,信息摘要算法
-
压缩性:任意长度的数据,算出的 MD5 值长度都是固定的;
-
容易计算:从原数据计算出 MD5 值很容易;
-
抗修改性:对原数据进行任何改动,哪怕只修改 1 个字节,所得到的 MD5 值都有很大区别;
-
强抗碰撞:想找到两个不同的数据,使它们具有相同的 MD5 值是非常困难的;
-
不可逆
@Test
void contextLoads() {
String s = DigestUtils.md5Hex("123456");
System.out.println(s);
// 盐值加密
System.out.println(Md5Crypt.md5Crypt("123456".getBytes()));
System.out.println(Md5Crypt.md5Crypt("123456".getBytes(), "$1$qqqqqqqq"));
// Spring 盐值加密(使用这个方式加密)
BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
//$2a$10$GT0TjB5YK5Vx77Y.2N7hkuYZtYAjZjMlE6NWGE2Aar/7pk/Rmhf8S
//$2a$10$cR3lis5HQQsQSSh8/c3L3ujIILXkVYmlw28vLA39xz4mHDN/NBVUi
String encode = bCryptPasswordEncoder.encode("123456");
boolean matches = bCryptPasswordEncoder.matches("123456", "$2a$10$GT0TjB5YK5Vx77Y.2N7hkuYZtYAjZjMlE6NWGE2Aar/7pk/Rmhf8S");
System.out.println(encode + "==>" + matches);
}