认证服务


创建认证模块

Snipaste_2022-03-07_21-49-27

Snipaste_2022-03-07_21-53-52

pom.xml

<?xml version="1.0" encoding="UTF-8"?>

    4.0.0
    
        org.springframework.boot
        spring-boot-starter-parent
        2.1.8.RELEASE
         
    
    com.atguigu.gulimall
    gulimall-auth-server
    0.0.1-SNAPSHOT
    gulimall-auth-server
    gulimall-auth-server
    
        11
        Greenwich.SR3
    
    
        
            com.atguigu.gulimall
            gulimall-common
            0.0.1-SNAPSHOT
            
                
                    com.baomidou
                    mybatis-plus-boot-starter
                
            
        
        
            org.springframework.boot
            spring-boot-starter-thymeleaf
        
        
            org.springframework.boot
            spring-boot-starter-web
        
        
            org.springframework.cloud
            spring-cloud-starter-openfeign
        

        
            org.springframework.boot
            spring-boot-devtools
            runtime
            true
        
        
            org.projectlombok
            lombok
            true
        
        
            org.springframework.boot
            spring-boot-starter-test
            test
        
    
    
        
            
                org.springframework.cloud
                spring-cloud-dependencies
                ${spring-cloud.version}
                pom
                import
            
        
    

    
        
            
                org.springframework.boot
                spring-boot-maven-plugin
                
                    
                        
                            org.projectlombok
                            lombok
                        
                    
                
            
        
    



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

Snipaste_2022-03-07_22-21-09

页面及域名访问初始化

修改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 动静分离配置。最终目录:

Snipaste_2022-03-07_22-26-45

Snipaste_2022-03-07_22-27-03

Snipaste_2022-03-07_22-27-26

认证服务-短信验证码

阿里云短信服务

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;

/**
 * 

Title: SmsComponent

* 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 headers = new HashMap(); // //最后在header中的格式(中间是英文空格)为Authorization:APPCODE 83359fd73fe94948385f570e3c139105 // headers.put("Authorization", "APPCODE " + this.appCode); // Map querys = new HashMap(); // 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();
}

认证服务-登录注册功能

参数校验补充

通过注解可以给前端传递过来的值进行校验,例如:

Snipaste_2022-03-08_20-52-53

但是这个注解必须配合 @Valid 使用,完成对参数的校验:

Snipaste_2022-03-08_20-54-03

而校验的结果,也会自动封装到 BindingResult 类型中,通过这个参数可以很方便的对错误的参数进行处理。

hasErrors() 可以判断是否有参数校验错误,如果有,可以通过 getFieldsErrors() 方法获取错误列表。

Snipaste_2022-03-08_20-54-46

注意:@Valid 和 BindingResult 是一一对应的,多个 @Valid 标注的参数实体后面要对应各自的 BindingResult.

SpringMVC 重定向携带数据

Snipaste_2022-03-08_20-55-37

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);
}