Realize mobile phone login (SMS verification)

Login requirements

1. Login takes the form of pop-up layer
2. Login method:
(1) Mobile phone number + mobile phone verification code
(2) Wechat scanning
3. There is no registration interface. The first login determines whether the system exists according to the mobile phone number. If it does not exist, it will be registered automatically
4. If wechat scanning and login is successful, you must bind the mobile phone number, that is, bind the mobile phone number after the first successful scanning, and then log in directly after subsequent scanning
5. The gateway judges the login status in a unified way. How to log in will pop up the login layer on the page

Login implementation

1. Build the service user module
1.1 build service user module
Refer to the service Hosp module for the construction process
1.2 modify configuration
1. Modify 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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>service</artifactId>
        <groupId>com.atguigu</groupId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>service_user</artifactId>

    <dependencies>
        <dependency>
            <groupId>com.atguigu</groupId>
            <artifactId>service_cmn_client</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>
    </dependencies>
</project>

2. Add configuration file application properties

# Service port
server.port=8203
# service name
spring.application.name=service-user

# Environment settings: dev, test, prod
spring.profiles.active=dev

# mysql database connection
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://192.168.44.165:3306/yygh_user?characterEncoding=utf-8&useSSL=false
spring.datasource.username=root
spring.datasource.password=root123

#Returns the global time format of json
spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
spring.jackson.time-zone=GMT+8

# nacos service address
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848

#The path to the configuration mapper xml file
mybatis-plus.mapper-locations=classpath:com/atguigu/yygh/user/mapper/xml/*.xml

1.3 startup

@SpringBootApplication
@ComponentScan(basePackages = "com.atguigu")
@EnableDiscoveryClient
@EnableFeignClients(basePackages = "com.atguigu")
public class ServiceUserApplication {
    public static void main(String[] args) {
        SpringApplication.run(ServiceUserApplication.class, args);
    }
}

1.4 configure gateway

#Set routing id
spring.cloud.gateway.routes[2].id=service-user
#Set the uri of the route
spring.cloud.gateway.routes[2].uri=lb://service-user
#Set the route assertion. The agent servicerId is the / auth / path of auth service
spring.cloud.gateway.routes[2].predicates= Path=/*/user/**

Add user base class

2.1 adding a model
Note: since entity objects have no logic, we have imported them uniformly
com.atguigu.yygh.model.user.UserInfo
2.2 adding Mapper
1. Add com atguigu. yygh. user. UserInfoMapper

import com.atguigu.yygh.model.user.UserInfo;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;

public interface UserInfoMapper extends BaseMapper<UserInfo> {
}

2. Add userinfomapper xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.atguigu.yygh.user.mapper.UserInfoMapper">

</mapper>

2.3 add service interface and implementation class
1. Add com atguigu. yygh. user. service. Userinfoservice interface

public interface UserInfoService extends IService<UserInfo> {
}

2. Add com atguigu. yygh. user. service. impl. Userinfoserviceimpl interface implementation

@Service
public class UserInfoServiceImpl extends
        ServiceImpl<UserInfoMapper, UserInfo> implements UserInfoService {
}

2.4 add controller
Add com atguigu. yygh. user. api. Userinfoapicontroller class

@RestController
@RequestMapping("/api/user")
public class UserInfoApiController {

    @Autowired
    private UserInfoService userInfoService;
}

3. Login api interface
3.1 adding service interface and Implementation
1. Add an interface to the UserInfoService class

//Member login
Map<String, Object> login(LoginVo loginVo);

2. Implement the interface in UserInfoServiceImpl class

@Service
public class UserInfoServiceImpl extends
        ServiceImpl<UserInfoMapper, UserInfo> implements UserInfoService {
    @Override
    public Map<String, Object> login(LoginVo loginVo) {
        String phone = loginVo.getPhone();
        String code = loginVo.getCode();
        //Calibration parameters
        if(StringUtils.isEmpty(phone) ||
                StringUtils.isEmpty(code)) {
            throw new YyghException(ResultCodeEnum.PARAM_ERROR);
        }

        //TODO verification code

        //The phone number is already in use
        QueryWrapper<UserInfo> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("phone", phone);
        //Get members
        UserInfo userInfo = baseMapper.selectOne(queryWrapper);
        if(null == userInfo) {
            userInfo = new UserInfo();
            userInfo.setName("");
            userInfo.setPhone(phone);
            userInfo.setStatus(1);
            this.save(userInfo);
        }
        //Check whether the is disabled
        if(userInfo.getStatus() == 0) {
            throw new YyghException(ResultCodeEnum.LOGIN_DISABLED_ERROR);
        }

        //TODO log in

        //Return page display name
        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);
        map.put("token", "");
        return map;
    }
}

explain:
1. The verification code is annotated first and verified later
2. The login succeeded in generating a token, which will be explained later
3.2 add controller interface
1. Add a method in the UserInfoApiController class

@ApiOperation(value = "Member login")
@PostMapping("login")
public Result login(@RequestBody LoginVo loginVo, HttpServletRequest request) {
    loginVo.setIp(IpUtil.getIpAddr(request));
    Map<String, Object> info = userInfoService.login(loginVo);
    return Result.ok(info);
}

2. Add IpUtil tool class

public class IpUtil {
    private static final String UNKNOWN = "unknown";
    private static final String LOCALHOST = "127.0.0.1";
    private static final String SEPARATOR = ",";

    public static String getIpAddr(HttpServletRequest request) {
        System.out.println(request);
        String ipAddress;
        try {
            ipAddress = request.getHeader("x-forwarded-for");
            if (ipAddress == null || ipAddress.length() == 0 || UNKNOWN.equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getHeader("Proxy-Client-IP");
            }
            if (ipAddress == null || ipAddress.length() == 0 || UNKNOWN.equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getHeader("WL-Proxy-Client-IP");
            }
            if (ipAddress == null || ipAddress.length() == 0 || UNKNOWN.equalsIgnoreCase(ipAddress)) {
                ipAddress = request.getRemoteAddr();
                if (LOCALHOST.equals(ipAddress)) {
                    InetAddress inet = null;
                    try {
                        inet = InetAddress.getLocalHost();
                    } catch (UnknownHostException e) {
                        e.printStackTrace();
                    }
                    ipAddress = inet.getHostAddress();
                }
            }
            // In the case of multiple agents, the first IP is the real IP of the client, and multiple IPS are divided according to ','
            // "***.***.***.***".length()
            if (ipAddress != null && ipAddress.length() >15) {
                if (ipAddress.indexOf(SEPARATOR) >0) {
                    ipAddress = ipAddress.substring(0, ipAddress.indexOf(","));
                }
            }
        } catch (Exception e) {
            ipAddress = "";
        }
        return ipAddress;
    }
}

JWT generates a token

JWT tools
JWT (Json Web Token) is a JSON based open standard implemented to transfer declarations between network application environments.
The declaration of JWT is generally used to transfer authenticated user identity information between identity providers and service providers, so as to obtain resources from the resource server. For example, it is used for user login

The most important role of JWT is the anti-counterfeiting of token information.

The principle of JWT,
A JWT consists of three parts: public part, private part and signature part. Finally, the JWT is obtained by base64 coding.

1. Public part
It mainly refers to the relevant configuration parameters of the JWT, such as signature encryption algorithm, format type, expiration time, etc.
Key=ATGUIGU
2. Private part
User defined content, information to be encapsulated according to actual needs.
userInfo {user Id, user nickName nickname}
3. Signature part
SaltiP: ip address of the current server! {ip address for configuring proxy server in linux}
The main user encrypts {salt value} when generating strings for JWT
Finally, the key+salt+userInfo token is formed!
base64 encoding is not encryption, but turns the plaintext information into an invisible string. But in fact, base64 can be encoded into plaintext with some tools, so don't put private information in JWT.
5.2 integrated JWT
1. In the common util module POM XML add dependency

<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
</dependency>

Version already exists in yygh parent module POM XML add
2. Write the JwtHelper class in the common util module

public class JwtHelper {
    private static long tokenExpiration = 24*60*60*1000;
    private static String tokenSignKey = "123456";

    public static String createToken(Long userId, String userName) {
        String token = Jwts.builder()
                .setSubject("YYGH-USER")
                .setExpiration(new Date(System.currentTimeMillis() + tokenExpiration))
                .claim("userId", userId)
                .claim("userName", userName)
                .signWith(SignatureAlgorithm.HS512, tokenSignKey)
                .compressWith(CompressionCodecs.GZIP)
                .compact();
        return token;
    }
    public static Long getUserId(String token) {
        if(StringUtils.isEmpty(token)) return null;
        Jws<Claims> claimsJws = Jwts.parser().setSigningKey(tokenSignKey).parseClaimsJws(token);
        Claims claims = claimsJws.getBody();
        Integer userId = (Integer)claims.get("userId");
        return userId.longValue();
    }
    public static String getUserName(String token) {
        if(StringUtils.isEmpty(token)) return "";
        Jws<Claims> claimsJws 
= Jwts.parser().setSigningKey(tokenSignKey).parseClaimsJws(token);
        Claims claims = claimsJws.getBody();
        return (String)claims.get("userName");
    }
    public static void main(String[] args) {
        String token = JwtHelper.createToken(1L, "55");
        System.out.println(token);
        System.out.println(JwtHelper.getUserId(token));
        System.out.println(JwtHelper.getUserName(token));
    }
}

Description: execute the main method test
5.3 improve the login service interface
Modify UserInfoServiceImpl class login method

public Map<String, Object> loginUser(LoginVo loginVo) {
  ............
    //jwt generates token string
    String token = JwtHelper.createToken(userInfo.getId(), name);
    map.put("token",token);
    return map;
}

Build service MSM module

6.2. 1. Build the service MSM module
Refer to the service Hosp module for the construction process
6.2. 2. Modify the configuration
1. Modify POM xml

<dependencies>
    <dependency>
        <groupId>com.aliyun</groupId>
        <artifactId>aliyun-java-sdk-core</artifactId>
    </dependency>
</dependencies>

2. Add configuration file application properties

# Service port
server.port=8204
# service name
spring.application.name=service-msm

#Returns the global time format of json
spring.jackson.date-format=yyyy-MM-dd HH:mm:ss
spring.jackson.time-zone=GMT+8

spring.redis.host=192.168.44.165
spring.redis.port=6379
spring.redis.database= 0
spring.redis.timeout=1800000
spring.redis.lettuce.pool.max-active=20
spring.redis.lettuce.pool.max-wait=-1
#Maximum blocking waiting time (negative number indicates no limit)
spring.redis.lettuce.pool.max-idle=5
spring.redis.lettuce.pool.min-idle=0

# nacos service address
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
        
aliyun.sms.regionId=default
aliyun.sms.accessKeyId=LT6I0Y5633pX89qC
aliyun.sms.secret=jX8D04Dm12I3gGKj345FYSzu0fq8mT

6.2. 3 startup class

@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)//Cancel data source auto configuration
@EnableDiscoveryClient
public class ServiceMsmApplication {
   public static void main(String[] args) {
      SpringApplication.run(ServiceMsmApplication.class, args);
   }
}

6.2. 4 configure gateway

#Set routing id
spring.cloud.gateway.routes[3].id=service-msm
#Set the uri of the route
spring.cloud.gateway.routes[3].uri=lb://service-msm
#Set the route assertion. The agent servicerId is the / auth / path of auth service
spring.cloud.gateway.routes[3].predicates= Path=/*/msm/**

6.3 interface for encapsulating and registering SMS verification code
6.3. 1 add configuration class

@Component
public class ConstantPropertiesUtils implements InitializingBean {

    @Value("${aliyun.sms.regionId}")
    private String regionId;

    @Value("${aliyun.sms.accessKeyId}")
    private String accessKeyId;

    @Value("${aliyun.sms.secret}")
    private String secret;

    public static String REGION_Id;
    public static String ACCESS_KEY_ID;
    public static String SECRECT;

    @Override
    public void afterPropertiesSet() throws Exception {
        REGION_Id=regionId;
        ACCESS_KEY_ID=accessKeyId;
        SECRECT=secret;
    }
}

6.3. 2 encapsulate service interface and implementation class

public interface MsmService {

    //Send mobile verification code
    boolean send(String phone, String code);
}

@Service
public class MsmServiceImpl implements MsmService {
    @Override
    public boolean send(String phone, String code) {
        //Judge whether the mobile phone number is empty
        if(StringUtils.isEmpty(phone)) {
            return false;
        }
        //Integrate Alibaba cloud SMS services
        //Set relevant parameters
        DefaultProfile profile = DefaultProfile.
                getProfile(ConstantPropertiesUtils.REGION_Id,
                        ConstantPropertiesUtils.ACCESS_KEY_ID,
                        ConstantPropertiesUtils.SECRECT);
        IAcsClient client = new DefaultAcsClient(profile);
        CommonRequest request = new CommonRequest();
        //request.setProtocol(ProtocolType.HTTPS);
        request.setMethod(MethodType.POST);
        request.setDomain("dysmsapi.aliyuncs.com");
        request.setVersion("2017-05-25");
        request.setAction("SendSms");

        //cell-phone number
        request.putQueryParameter("PhoneNumbers", phone);
        //Signature name
        request.putQueryParameter("SignName", "My grain online education website");
        //Template code
        request.putQueryParameter("TemplateCode", "SMS_180051135");
        //The verification code uses json format {"code":"123456"}
        Map<String,Object> param = new HashMap();
        param.put("code",code);
        request.putQueryParameter("TemplateParam", JSONObject.toJSONString(param));

        //Call method to send SMS
        try {
            CommonResponse response = client.getCommonResponse(request);
            System.out.println(response.getData());
            return response.getHttpResponse().isSuccess();
        } catch (ServerException e) {
            e.printStackTrace();
        } catch (ClientException e) {
            e.printStackTrace();
        }
        return false;
    }
}

6.3. 3. Encapsulate the controller interface

@RestController
@RequestMapping("/api/msm")
public class MsmApiController {

    @Autowired
    private MsmService msmService;

    @Autowired
    private RedisTemplate<String,String> redisTemplate;

    //Send mobile verification code
    @GetMapping("send/{phone}")
    public Result sendCode(@PathVariable String phone) {
        //Obtain the verification code from redis. If the verification code is obtained, return ok
        // key mobile number value verification code
        String code = redisTemplate.opsForValue().get(phone);
        if(!StringUtils.isEmpty(code)) {
            return Result.ok();
        }
        //If you can't get it from redis,
        // Generate verification code,
        code = RandomUtil.getSixBitRandom();
        //Call the service method to send by integrating the SMS service
        boolean isSend = msmService.send(phone,code);
        //Generate a verification code, put it in redis, and set the effective time
        if(isSend) {
            redisTemplate.opsForValue().set(phone,code,2, TimeUnit.MINUTES);
            return Result.ok();
        } else {
            return Result.fail().message("Sending SMS failed");
        }
    }
}

Tool class

public class RandomUtil {

    private static final Random random = new Random();

    private static final DecimalFormat fourdf = new DecimalFormat("0000");

    private static final DecimalFormat sixdf = new DecimalFormat("000000");

    public static String getFourBitRandom() {
        return fourdf.format(random.nextInt(10000));
    }

    public static String getSixBitRandom() {
        return sixdf.format(random.nextInt(1000000));
    }

    /**
     * Given an array, extract n data
     * @param list
     * @param n
     * @return
     */
    public static ArrayList getRandom(List list, int n) {

        Random random = new Random();

        HashMap<Object, Object> hashMap = new HashMap<Object, Object>();

// Generate random numbers and store them in HashMap
        for (int i = 0; i < list.size(); i++) {

            int number = random.nextInt(100) + 1;

            hashMap.put(number, i);
        }

// Importing arrays from HashMap
        Object[] robjs = hashMap.values().toArray();

        ArrayList r = new ArrayList();

// Traverse the array and print the data
        for (int i = 0; i < n; i++) {
            r.add(list.get((int) robjs[i]));
            System.out.print(list.get((int) robjs[i]) + "\t");
        }
        System.out.print("\n");
        return r;
    }
}

6.3. 4. Improve the login service interface
Modify the login method of UserInfoServiceImpl class and write verification code

//Verification code
String mobleCode = redisTemplate.opsForValue().get(phone);
if(!code.equals(mobleCode)) {
    throw new YyghException(ResultCodeEnum.CODE_ERROR);
}

Keywords: Java Spring Spring Cloud

Added by minds_gifts on Fri, 31 Dec 2021 13:18:58 +0200