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