一、概念介绍
1、单点登陆(简称SSO)
单点登陆(SSO) |
---|
单点登陆就是,当在某个模块中登陆后,转到本项目其它模块时,无需二次登陆 |
单点登陆,适用于分布式系统,也就是我们当前这个项目,每个模块单独部署到一台服务器 |
session广播机制 |
---|
通过为每一个模块复制当前登陆后的session对象实现 |
缺点:目前一个项目一般都有多个模块,每次登陆都要复制一次,及其浪费资源 |
cookie+redis |
---|
当我们在某一个模块登陆后,会把数据放到cookie和redis中 |
其中redis会生成唯一key值,对应value中会存储用户数据 |
最后把redis中的key值放到cookie里面 |
每次发送请求,都会带cookie对象,通过解析cookie中的key,获取redis中对应的value,得到用户登陆信息。最终也就实现了SSO |
session默认30分钟过期,我们使用cookie+redis或者token两种方式,也可以通过一些手段,实现规定时间后过期功能 |
token |
---|
按照一定规则(编码和加密)生成字符串,字符串中包含用户登陆信息 |
将字符串通过cookie返回或者通过地址栏返回给后台 |
去其他模块的时候,就根据返回的字符串利用反规则解码出登陆信息实SSO |
2、JWT
JWT(令牌加密) |
---|
令牌,token形式的单点登陆可以通过JWT来实现 |
自包含令牌:就是按照一定规则生成的字符串,包含所需信息 |
JWT:就是一种通用的规则,已经规定好了规则,使用jwt可以直接生成字符串,包含我们需要的信息 |
JWT生成字符串规则(包含3部分) |
---|
1、jwt头信息 |
2、有效荷载,包含主体信息(用户信息) |
3、签名哈希,防伪标志 |
二、整合JWT
1、引入JWT依赖
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
2、编写工具类
package com.yzpnb.common_utils;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.util.StringUtils;
import javax.servlet.http.HttpServletRequest;
import java.util.Date;
public class JwtUtils {
public static final long EXPIRE = 1000 * 60 * 60 * 24;
public static final String APP_SECRET = "ukc8BDbRigUDaY6pZFfWus2jZWLPHO";
public static String getJwtToken(String id, String nickname){
String JwtToken = Jwts.builder()
.setHeaderParam("typ", "JWT")
.setHeaderParam("alg", "HS256")
.setSubject("guli-user")
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + EXPIRE))
.claim("id", id)
.claim("nickname", nickname)
.signWith(SignatureAlgorithm.HS256, APP_SECRET)
.compact();
return JwtToken;
}
public static boolean checkToken(String jwtToken) {
if(StringUtils.isEmpty(jwtToken)) return false;
try {
Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
public static boolean checkToken(HttpServletRequest request) {
try {
String jwtToken = request.getHeader("token");
if(StringUtils.isEmpty(jwtToken)) return false;
Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
public static String getMemberIdByJwtToken(HttpServletRequest request) {
String jwtToken = request.getHeader("token");
if(StringUtils.isEmpty(jwtToken)) return "";
Jws<Claims> claimsJws = Jwts.parser().setSigningKey(APP_SECRET).parseClaimsJws(jwtToken);
Claims claims = claimsJws.getBody();
return (String)claims.get("id");
}
}
三、整合阿里云短信服务
1、阿里云短信服务开通
2、申请模板和签名
3、新建一个微服务引入依赖
<!--fastjson-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.70</version>
</dependency>
<!--阿里云依赖-->
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-core</artifactId>
<version>4.5.1</version>
</dependency>
4、配置application.yml
server:
port: 8005
spring:
application:
name: service-msm
profiles:
active: dev
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
redis:
host: 127.0.0.1
port: 6379
database: 0
timeout: 1800000
lettuce:
pool:
max-active: 20
max-wait: -1
max-idle: 5
min-idle: 0
5、编写controller
注意:阿里云短信验证码,不会帮我们默认生成验证码,只能帮我们将短信发送,所以验证码需要自己生成 |
---|
package com.yzpnb.msmservice.controller;
import com.yzpnb.common_utils.Result;
import com.yzpnb.msmservice.service.MsmService;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import java.text.DecimalFormat;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.TimeUnit;
@RestController
@RequestMapping("/msmservice/")
@CrossOrigin
public class MsmController {
@Autowired
private MsmService msmService;
@Autowired
private RedisTemplate<String,String> redisTemplate;
@ApiOperation("根据手机号发送短信")
@GetMapping("shortMessage/{phoneNumber}")
public Result shortMessage(@ApiParam(name = "phoneNumber",value = "电话号")
@PathVariable String phoneNumber){
String s = redisTemplate.opsForValue().get(phoneNumber);
if(!StringUtils.isEmpty(s)){
return Result.ok();
}
DecimalFormat fourdf=new DecimalFormat("0000");
String code = fourdf.format(new Random().nextInt(10000));
Map<String,Object> map=new HashMap<>();
map.put("code",code);
Boolean flag = msmService.shortMessage(phoneNumber, map);
if (flag==true){
redisTemplate.opsForValue().set(phoneNumber,code,5, TimeUnit.MINUTES);
return Result.ok();
}else{
return Result.error().message("发送失败");
}
}
}
6、编写service
package com.yzpnb.msmservice.service.impl;
import com.alibaba.fastjson.JSONObject;
import com.aliyuncs.CommonRequest;
import com.aliyuncs.CommonResponse;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.http.MethodType;
import com.aliyuncs.profile.DefaultProfile;
import com.yzpnb.msmservice.service.MsmService;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import java.util.Map;
@Service
public class MsmServiceImpl implements MsmService {
@Override
public Boolean shortMessage(String phoneNumber, Map<String, Object> map) {
if(StringUtils.isEmpty(phoneNumber)) return false;
DefaultProfile profile=DefaultProfile.getProfile("default",
"你的密钥id",
"你的密钥");
IAcsClient acsClient = 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", phoneNumber);
request.putQueryParameter("SignName", "我的农大线上学院网站");
request.putQueryParameter("TemplateCode", "你的模板CODE");
request.putQueryParameter("TemplateParam", JSONObject.toJSONString(map));
boolean b=false;
try {
CommonResponse commonResponse =acsClient.getCommonResponse(request);
b=commonResponse.getHttpResponse().isSuccess();
} catch (ClientException e) {
e.printStackTrace();
}
return b;
}
}
7、测试
四、登陆接口(后端)
1、环境搭建
1、数据库源码
CREATE TABLE `ucenter_member` (
`id` char(19) NOT NULL COMMENT '会员id',
`openid` varchar(128) DEFAULT NULL COMMENT '微信openid',
`mobile` varchar(11) DEFAULT '' COMMENT '手机号',
`password` varchar(255) DEFAULT NULL COMMENT '密码',
`nickname` varchar(50) DEFAULT NULL COMMENT '昵称',
`sex` tinyint(2) unsigned DEFAULT NULL COMMENT '性别 1 女,2 男',
`age` tinyint(3) unsigned DEFAULT NULL COMMENT '年龄',
`avatar` varchar(255) DEFAULT NULL COMMENT '用户头像',
`sign` varchar(100) DEFAULT NULL COMMENT '用户签名',
`is_disabled` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否禁用 1(true)已禁用, 0(false)未禁用',
`is_deleted` tinyint(1) NOT NULL DEFAULT '0' COMMENT '逻辑删除 1(true)已删除, 0(false)未删除',
`gmt_create` datetime NOT NULL COMMENT '创建时间',
`gmt_modified` datetime NOT NULL COMMENT '更新时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='会员表';
INSERT INTO `ucenter_member` VALUES ('1',NULL,'13700000001','96e79218965eb72c92a549dd5a330112','小三123',1,5,'http://thirdwx.qlogo.cn/mmopen/vi_32/DYAIOgq83eoj0hHXhgJNOTSOFsS4uZs8x1ConecaVOB8eIl115xmJZcT4oCicvia7wMEufibKtTLqiaJeanU2Lpg3w/132',NULL,0,0,'2019-01-01 12:11:33','2019-11-08 11:56:01'),('1080736474267144193',NULL,'13700000011','96e79218965eb72c92a549dd5a330112','用户XJtDfaYeKk',1,19,'http://thirdwx.qlogo.cn/mmopen/vi_32/DYAIOgq83eoj0hHXhgJNOTSOFsS4uZs8x1ConecaVOB8eIl115xmJZcT4oCicvia7wMEufibKtTLqiaJeanU2Lpg3w/132',NULL,0,0,'2019-01-02 12:12:45','2019-01-02 12:12:56'),('1080736474355224577',NULL,'13700000002','96e79218965eb72c92a549dd5a330112','用户wUrNkzAPrc',1,27,'http://thirdwx.qlogo.cn/mmopen/vi_32/DYAIOgq83eoj0hHXhgJNOTSOFsS4uZs8x1ConecaVOB8eIl115xmJZcT4oCicvia7wMEufibKtTLqiaJeanU2Lpg3w/132',NULL,0,0,'2019-01-02 12:13:56','2019-01-02 12:14:07'),('1086387099449442306',NULL,'13520191388','96e79218965eb72c92a549dd5a330112','用户XTMUeHDAoj',2,20,'http://thirdwx.qlogo.cn/mmopen/vi_32/DYAIOgq83eoj0hHXhgJNOTSOFsS4uZs8x1ConecaVOB8eIl115xmJZcT4oCicvia7wMEufibKtTLqiaJeanU2Lpg3w/132',NULL,0,0,'2019-01-19 06:17:23','2019-01-19 06:17:23'),('1086387099520745473',NULL,'13520191389','96e79218965eb72c92a549dd5a330112','用户vSdKeDlimn',1,21,'http://thirdwx.qlogo.cn/mmopen/vi_32/DYAIOgq83eoj0hHXhgJNOTSOFsS4uZs8x1ConecaVOB8eIl115xmJZcT4oCicvia7wMEufibKtTLqiaJeanU2Lpg3w/132',NULL,0,0,'2019-01-19 06:17:23','2019-01-19 06:17:23'),('1086387099608825858',NULL,'13520191381','96e79218965eb72c92a549dd5a330112','用户EoyWUVXQoP',1,18,'http://thirdwx.qlogo.cn/mmopen/vi_32/DYAIOgq83eoj0hHXhgJNOTSOFsS4uZs8x1ConecaVOB8eIl115xmJZcT4oCicvia7wMEufibKtTLqiaJeanU2Lpg3w/132',NULL,0,0,'2019-01-19 06:17:23','2019-01-19 06:17:23'),('1086387099701100545',NULL,'13520191382','96e79218965eb72c92a549dd5a330112','用户LcAYbxLNdN',2,24,'http://thirdwx.qlogo.cn/mmopen/vi_32/DYAIOgq83eoj0hHXhgJNOTSOFsS4uZs8x1ConecaVOB8eIl115xmJZcT4oCicvia7wMEufibKtTLqiaJeanU2Lpg3w/132',NULL,0,0,'2019-01-19 06:17:23','2019-01-19 06:17:23'),('1086387099776598018',NULL,'13520191383','96e79218965eb72c92a549dd5a330112','用户dZdjcgltnk',2,25,'http://thirdwx.qlogo.cn/mmopen/vi_32/DYAIOgq83eoj0hHXhgJNOTSOFsS4uZs8x1ConecaVOB8eIl115xmJZcT4oCicvia7wMEufibKtTLqiaJeanU2Lpg3w/132',NULL,0,0,'2019-01-19 06:17:23','2019-01-19 06:17:23'),('1086387099852095490',NULL,'13520191384','96e79218965eb72c92a549dd5a330112','用户wNHGHlxUwX',2,23,'http://thirdwx.qlogo.cn/mmopen/vi_32/DYAIOgq83eoj0hHXhgJNOTSOFsS4uZs8x1ConecaVOB8eIl115xmJZcT4oCicvia7wMEufibKtTLqiaJeanU2Lpg3w/132',NULL,0,0,'2019-01-19 06:17:23','2019-01-19 06:17:23'),('1106746895272849410','o1R-t5u2TfEVeVjO9CPGdHPNw-to',NULL,NULL,'檀梵\'',NULL,NULL,'http://thirdwx.qlogo.cn/mmopen/vi_32/zZfLXcetf2Rpsibq6HbPUWKgWSJHtha9y1XBeaqluPUs6BYicW1FJaVqj7U3ozHd3iaodGKJOvY2PvqYTuCKwpyfQ/132',NULL,0,0,'2019-03-16 10:39:57','2019-03-16 10:39:57'),('1106822699956654081',NULL,NULL,NULL,NULL,NULL,NULL,'http://thirdwx.qlogo.cn/mmopen/vi_32/DYAIOgq83eoj0hHXhgJNOTSOFsS4uZs8x1ConecaVOB8eIl115xmJZcT4oCicvia7wMEufibKtTLqiaJeanU2Lpg3w/132',NULL,0,0,'2019-03-16 15:41:10','2019-03-16 15:41:10'),('1106823035660357634','o1R-t5i4gENwHYRb5lVFy98Z0bdk',NULL,NULL,'GaoSir',NULL,NULL,'http://thirdwx.qlogo.cn/mmopen/vi_32/Q0j4TwGTfTJI53RcCuc1no02os6ZrattWGiazlPnicoZQ59zkS7phNdLEWUPDk8fzoxibAnXV1Sbx0trqXEsGhXPw/132',NULL,0,0,'2019-03-16 15:42:30','2019-03-16 15:42:30'),('1106823041599492098',NULL,NULL,NULL,NULL,NULL,NULL,'http://thirdwx.qlogo.cn/mmopen/vi_32/DYAIOgq83eoj0hHXhgJNOTSOFsS4uZs8x1ConecaVOB8eIl115xmJZcT4oCicvia7wMEufibKtTLqiaJeanU2Lpg3w/132',NULL,0,0,'2019-03-16 15:42:32','2019-03-16 15:42:32'),('1106823115788341250','o1R-t5l_3rnbZbn4jWwFdy6Gk6cg',NULL,NULL,'换个网名哇、',NULL,NULL,'http://thirdwx.qlogo.cn/mmopen/vi_32/jJHyeM0EN2jhB70LntI3k8fEKe7W6CwykrKMgDJM4VZqCpcxibVibX397p0vmbKURGkLS4jxjGB0GpZfxCicgt07w/132',NULL,0,0,'2019-03-16 15:42:49','2019-03-16 15:42:49'),('1106826046730227714','o1R-t5gyxumyBqt0CWcnh0S6Ya1g',NULL,NULL,'我是Helen',NULL,NULL,'http://thirdwx.qlogo.cn/mmopen/vi_32/Q0j4TwGTfTKDRfib8wy7A2ltERKh4VygxdjVC1x5OaOb1t9hot4JNt5agwaVLdJLcD9vJCNcxkvQnlvLYIPfrZw/132',NULL,0,0,'2019-03-16 15:54:28','2019-03-16 15:54:28'),('1106828185829490690','o1R-t5nNlou5lRwBVgGNJFm4rbc4',NULL,NULL,' 虎头',NULL,NULL,'http://thirdwx.qlogo.cn/mmopen/vi_32/Q0j4TwGTfTKxCqRzuYWQmpwiaqQEjNxbC7WicebicXQusU306jgmfoOzUcFg1qaDq5BStiblwBjw5dUOblQ2gUicQOQ/132',NULL,0,0,'2019-03-16 16:02:58','2019-03-16 16:02:58'),('1106830599651442689','o1R-t5hZHQB1cbX7HZJsiM727_SA',NULL,NULL,'是吴啊',NULL,NULL,'http://thirdwx.qlogo.cn/mmopen/vi_32/Q0j4TwGTfTJ9CsqApybcs7f3Dyib9IxIh0sBqJb7LicbjU4WticJFF0PVwFvHgtbFdBwfmk3H2t3NyqmEmVx17tRA/132',NULL,0,0,'2019-03-16 16:12:34','2019-03-16 16:12:34'),('1106830976199278593','o1R-t5meKOoyEJ3-IhWRCBKFcvzU',NULL,NULL,'我才是Helen',NULL,NULL,'http://thirdwx.qlogo.cn/mmopen/vi_32/DYAIOgq83epMicP9UT6mVjYWdno0OJZkOXiajG0sllJTbGJ9DYiceej2XvbDSGCK8LCF7jv1PuG2uoYlePWic9XO8A/132',NULL,0,0,'2019-03-16 16:14:03','2019-03-16 16:14:03'),('1106831936900415490','o1R-t5jXYSWakGtnUBnKbfVT5Iok',NULL,NULL,'文若姬',NULL,NULL,'http://thirdwx.qlogo.cn/mmopen/vi_32/3HEmJwpSzguqqAyzmBwqT6aicIanswZibEOicQInQJI3ZY1qmu59icJC6N7SahKqWYv24GvX5KH2fibwt0mPWcTJ3fg/132',NULL,0,0,'2019-03-16 16:17:52','2019-03-16 16:17:52'),('1106832491064442882','o1R-t5sud081Qsa2Vb2xSKgGnf_g',NULL,NULL,'Peanut',NULL,NULL,'http://thirdwx.qlogo.cn/mmopen/vi_32/DYAIOgq83eoj0hHXhgJNOTSOFsS4uZs8x1ConecaVOB8eIl115xmJZcT4oCicvia7wMEufibKtTLqiaJeanU2Lpg3w/132',NULL,0,0,'2019-03-16 16:20:04','2019-03-16 16:20:04'),('1106833021442510849','o1R-t5lsGc3I8P5bDpHj7m_AIRvQ',NULL,NULL,'食物链终结者',NULL,NULL,'http://thirdwx.qlogo.cn/mmopen/vi_32/MQ7qUmCprK9am16M1Ia1Cs3RK0qiarRrl9y8gsssBjIZeS2GwKSrnq7ZYhmrzuzDwBxSMMAofrXeLic9IBlW4M3Q/132',NULL,0,0,'2019-03-16 16:22:11','2019-03-16 16:22:11'),('1191600824445046786',NULL,'15210078344','96e79218965eb72c92a549dd5a330112','IT妖姬',1,5,'http://thirdwx.qlogo.cn/mmopen/vi_32/DYAIOgq83eoj0hHXhgJNOTSOFsS4uZs8x1ConecaVOB8eIl115xmJZcT4oCicvia7wMEufibKtTLqiaJeanU2Lpg3w/132',NULL,0,0,'2019-11-05 14:19:10','2019-11-08 18:04:43'),('1191616288114163713',NULL,'17866603606','96e79218965eb72c92a549dd5a330112','xiaowu',NULL,NULL,'http://thirdwx.qlogo.cn/mmopen/vi_32/DYAIOgq83eoj0hHXhgJNOTSOFsS4uZs8x1ConecaVOB8eIl115xmJZcT4oCicvia7wMEufibKtTLqiaJeanU2Lpg3w/132',NULL,0,0,'2019-11-05 15:20:37','2019-11-05 15:20:37'),('1195187659054329857',NULL,'15010546384','96e79218965eb72c92a549dd5a330112','qy',NULL,NULL,'http://thirdwx.qlogo.cn/mmopen/vi_32/DYAIOgq83eoj0hHXhgJNOTSOFsS4uZs8x1ConecaVOB8eIl115xmJZcT4oCicvia7wMEufibKtTLqiaJeanU2Lpg3w/132',NULL,0,0,'2019-11-15 11:51:58','2019-11-15 11:51:58');
2、创建子模块
3、application.yml
server:
port: 8006
spring:
application:
name: service-ucenter
profiles:
active: dev
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/gulischool?serverTimeZone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=false&allowPublicKeyRetrieval=true
username: root
password: 123456
jackson:
date-format: yyyy-MM-DD HH:mm:ss
time-zone: GMT+8
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8848
feign:
client:
config:
default:
connect-timeout: 10000
read-timeout: 10000
hystrix:
enabled: true
hystrix:
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 6000
mybatis-plus:
mapper-locations: classpath:com/yzpnb/eduservice/mapper/xml/*.xml
4、使用代码生成器,生成表对应MVC层次结构(记住将参数改成你自己的)
package gennerator;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import com.baomidou.mybatisplus.generator.config.GlobalConfig;
import com.baomidou.mybatisplus.generator.config.PackageConfig;
import com.baomidou.mybatisplus.generator.config.StrategyConfig;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import org.junit.Test;
public class CodeGenerator {
@Test
public void run() {
AutoGenerator mpg = new AutoGenerator();
GlobalConfig gc = new GlobalConfig();
String projectPath = System.getProperty("user.dir");
gc.setOutputDir("D:\\IdeaProjects\\gulischool\\service\\service-ucenter" + "/src/main/java");
gc.setAuthor("testjava");
gc.setOpen(false);
gc.setFileOverride(false);
gc.setServiceName("%sService");
gc.setIdType(IdType.ID_WORKER_STR);
gc.setDateType(DateType.ONLY_DATE);
gc.setSwagger2(true);
mpg.setGlobalConfig(gc);
DataSourceConfig dsc = new DataSourceConfig();
dsc.setUrl("jdbc:mysql://localhost:3306/gulischool?serverTimeZone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=false");
dsc.setDriverName("com.mysql.cj.jdbc.Driver");
dsc.setUsername("root");
dsc.setPassword("123456");
dsc.setDbType(DbType.MYSQL);
mpg.setDataSource(dsc);
PackageConfig pc = new PackageConfig();
pc.setParent("com.yzpnb");
pc.setModuleName("ucenter_service");
pc.setController("controller");
pc.setEntity("entity");
pc.setService("service");
pc.setMapper("mapper");
mpg.setPackageInfo(pc);
StrategyConfig strategy = new StrategyConfig();
strategy.setInclude("ucenter_member");
strategy.setNaming(NamingStrategy.underline_to_camel);
strategy.setTablePrefix(pc.getModuleName() + "_");
strategy.setColumnNaming(NamingStrategy.underline_to_camel);
strategy.setEntityLombokModel(true);
strategy.setRestControllerStyle(true);
strategy.setControllerMappingHyphenStyle(true);
mpg.setStrategy(strategy);
mpg.execute();
}
}
5、给实体类加自动填充,给controller层加跨域
6、启动类
package com.yzpnb.ucenter_service;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
@SpringBootApplication
@ComponentScan("com.yzpnb")
@MapperScan("com.yzpnb.ucenter_service.mapper")
public class UcenterApplication {
public static void main(String[] args) {
SpringApplication.run(UcenterApplication.class,args);
}
}
2、编写MD5加密工具类
MD5 |
---|
目前最常见的一种密码加密形式 |
此加密的最大特点就是,只能加密,无法解密 |
我们想要判断输入的密码是否等于数据库中存储的加密后的密码 |
必须使用你设定的相同的MD5规则加密后,与数据库中数据比较 |
为什么无法解密 |
---|
很简单,加密的时候采用位运算,并在每个加密后的字符后面加上,加密前字符的全新加密格式 |
位运算后的数据,由于进1,补零,原来的值就不见了,无法通过反运算获取原值 |
所以就是把加密规则告诉你,你也无法反加密出原数据,因为你根本不知道,加密后消掉了几个1,补了多少个零 |
package com.yzpnb.common_utils;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class MD5 {
public static String encrypt(String strSrc){
char hexChars[]={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
byte[] bytes=strSrc.getBytes();
try {
MessageDigest md =MessageDigest.getInstance("MD5");
md.update(bytes);
bytes=md.digest();
int j=bytes.length;
char[] chars=new char[j*2];
int k=0;
for(int i=0;i<bytes.length;i++){
byte b=bytes[i];
chars[k++]=hexChars[b>>> 4 & 0xf];
chars[k++]=hexChars[b & 0xf];
}
return new String(chars);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
throw new RuntimeException("MD5加密出错!!+"+e);
}
}
}
3、编写controller接口
package com.yzpnb.ucenter_service.controller;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.yzpnb.common_utils.Result;
import com.yzpnb.ucenter_service.entity.UcenterMember;
import com.yzpnb.ucenter_service.service.UcenterMemberService;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/ucenter_service/ucenter-member")
@CrossOrigin
public class UcenterMemberController {
@Autowired
private UcenterMemberService ucenterMemberService;
@ApiOperation("登录,判断登录的手机号和密码是否正确,返回JWT加密后token字符串")
@PostMapping("login")
public Result login(@RequestBody UcenterMember ucenterMember){
String token=ucenterMemberService.login(ucenterMember);
return Result.ok().data("token",token);
}
}
4、service接口
package com.yzpnb.ucenter_service.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.yzpnb.common_utils.JwtUtils;
import com.yzpnb.common_utils.MD5;
import com.yzpnb.service_base_handler.CustomExceptionHandler;
import com.yzpnb.ucenter_service.entity.UcenterMember;
import com.yzpnb.ucenter_service.mapper.UcenterMemberMapper;
import com.yzpnb.ucenter_service.service.UcenterMemberService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
@Service
public class UcenterMemberServiceImpl extends ServiceImpl<UcenterMemberMapper, UcenterMember> implements UcenterMemberService {
@Override
public String login(UcenterMember ucenterMember) {
if(StringUtils.isEmpty(ucenterMember.getMobile()) || StringUtils.isEmpty(ucenterMember.getPassword())){
throw new CustomExceptionHandler(20001,"手机号或密码为空");
}
QueryWrapper<UcenterMember> queryWrapper=new QueryWrapper<>();
queryWrapper.eq("mobile",ucenterMember.getMobile());
UcenterMember one = baseMapper.selectOne(queryWrapper);
if(one !=null){
String password= MD5.encrypt(ucenterMember.getPassword());
if(!one.getPassword().equals(password)){
throw new CustomExceptionHandler(20001,"密码错误");
}
if(one.getIsDisabled()){
throw new CustomExceptionHandler(20001,"此账号现在被禁用,可能有其他人登录");
}
}else{
throw new CustomExceptionHandler(20001,"此手机号没有注册");
}
String token = JwtUtils.getJwtToken(one.getId(), one.getNickname());
return token;
}
}
5、测试
五、注册接口(后端,需要spring cloud的feign组件,跨服务调用阿里云短信微服务)
1、创建实体类,包含注册信息和验证码
package com.yzpnb.ucenter_service.entity.vo;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.springframework.stereotype.Component;
@Component
@Data
public class RegisterVo {
@ApiModelProperty(value = "手机号")
private String mobile;
@ApiModelProperty(value = "密码")
private String password;
@ApiModelProperty(value = "昵称")
private String nickname;
@ApiModelProperty(value = "验证码")
private String code;
}
2、controller
@ApiOperation("注册,判断注册手机号是否已经存在,密码需要加密存储,需要验证码正确才能注册成功")
@PostMapping("register")
public Result register(@ApiParam(name = "registerVo",value = "注册对象")
@RequestBody RegisterVo registerVo){
ucenterMemberService.register(registerVo);
return Result.ok();
}
3、service
@Autowired
RedisTemplate<String,String> redisTemplate=new RedisTemplate<>();
@Override
public void register(RegisterVo registerVo) {
String mobile=registerVo.getMobile();
String password=registerVo.getPassword();
String nickname=registerVo.getNickname();
String code=registerVo.getCode();
if(StringUtils.isEmpty(mobile) || StringUtils.isEmpty(password)){
throw new CustomExceptionHandler(20001,"手机号或密码为空");
}else{
QueryWrapper<UcenterMember> queryWrapper=new QueryWrapper<>();
queryWrapper.eq("mobile",mobile);
int count = baseMapper.selectCount(queryWrapper);
if(count>0){
throw new CustomExceptionHandler(20001,"此手机号已经被注册");
}
}
if(StringUtils.isEmpty(nickname)){
throw new CustomExceptionHandler(20001,"请输入昵称");
}else{
QueryWrapper<UcenterMember> queryWrapper=new QueryWrapper<>();
queryWrapper.eq("nickname",nickname);
int count = baseMapper.selectCount(queryWrapper);
if(count>0){
throw new CustomExceptionHandler(20001,"昵称重复");
}
}
if(redisTemplate.hasKey(mobile)==false) throw new CustomExceptionHandler(20001,"此手机号的验证码没有生成或已过期");
if(StringUtils.isEmpty(code)){
throw new CustomExceptionHandler(20001,"请输入验证码");
}else{
String value = redisTemplate.opsForValue().get(mobile);
if(!value.equals(code)){
throw new CustomExceptionHandler(20001,"验证码错误");
}
}
password=MD5.encrypt(password);
UcenterMember ucenterMember=new UcenterMember();
ucenterMember.setMobile(mobile);
ucenterMember.setPassword(password);
ucenterMember.setNickname(nickname);
ucenterMember.setIsDisabled(false);
ucenterMember.setAvatar("http://thirdwx.qlogo.cn/mmopen/vi_32/DYAIOgq83eoj0hHXhgJNOTSOFsS4uZs8x1ConecaVOB8eIl115xmJZcT4oCicvia7wMEufibKtTLqiaJeanU2Lpg3w/132");
baseMapper.insert(ucenterMember);
}
4、测试
六、根据token获取用户信息
1、controller
@ApiOperation("根据token获取用户信息")
@GetMapping("getUserInfo")
public Result getUserInfo(HttpServletRequest request){
String id = JwtUtils.getMemberIdByJwtToken(request);
UcenterMember ucenterMember = ucenterMemberService.getById(id);
return Result.ok().data("userInfo",ucenterMember);
}
七、登陆与注册(前端,需要element ui,js-cookie,vue-qriously)
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
Vue.use(ElementUI)
1、api接口,配置nginx
3、布局页面
<template>
<div class="sign">
<!--标题-->
<div class="logo">
<img src="~/assets/img/logo.png" alt="logo">
</div>
<!--表单-->
<nuxt/>
</div>
</template>
2、静态页面(代码全在GitHub中)
3、代码实现
1、注册(源代码到github中取)
2、登陆(因为我们登陆需要使用cookie,所以需要下载js-cookie插件)
1、下载js-cookie插件:npm install js-cookie |
---|
4、将token存入cookie,请求登陆接口,获取用户信息,然后将用户信息,存入cookie中 |
---|
注意这是第四步哦!但为我好理解,将这步放前面 |
{domain:localhost}:表示只要当前访问的是localhost,cookie都会传递,你可以改成ip地址 |
2、创建前端拦截器,拦截request请求,查看cookie中是否有token字符串,有就将字符串放到header(请求头中) |
---|
import axios from 'axios'
import cookie from 'js-cookie'
import { MessageBox, Message } from 'element-ui'
const service = axios.create({
baseURL: 'http://localhost:9001',
timeout: 20000
})
service.interceptors.request.use(
config => {
if (cookie.get('token')) {
config.headers['token'] = cookie.get('token');
}
return config
},
err => {
return Promise.reject(err);
})
service.interceptors.response.use(
response => {
if (response.data.code == 28004) {
console.log("response.data.resultCode是28004")
window.location.href="/login"
return
}else{
if (response.data.code !== 20000) {
if(response.data.code != 25000) {
Message({
message: response.data.message || 'error',
type: 'error',
duration: 5 * 1000
})
}
} else {
return response;
}
}
},
error => {
return Promise.reject(error.response)
});
export default service
3、根据token值调用接口,获取用户信息,将返回的信息存入cookie |
---|
八、OAuth2.0
1、OAuth2
针对特定问题的解决方案 |
---|
1、开放系统间授权问题 |
2、分布式访问问题 |
开放系统间授权问题 |
---|
举例:相片拥有者,将照片存储到云中,这时负责打印的,是没有访问云中照片的授权的,所以无法打印 |
而相片拥有者,这时通过授权,让打印机可以打印照片 |
方式1、适用于同一公司内部的多个系统,不适用于不受信的第三方应用 |
---|
方式3、接近OAuth2方式,需要考虑如何管理令牌、颁发令牌、吊销令牌,需要统一的协议,因此就有了OAuth2协议 |
---|
去哪里学习?《OAuth2最简向导》 |
---|
川崎高彦:OAuth2领域专家,开发了一个OAuth2 sass服务,OAuth2 as Service,并且做成了一个公司 |
再融资的过程中为了向投资人解释OAuth2是什么,于是写了一篇文章,《OAuth2最简向导》 |
2、OAuth2.0实践了解
3、OAuth2的误解
九、微信扫码登录(难度较高,选择性观看)
1、申请资质
1、开发者资质(看看就好,想要注册你需要有一个公司)
2、申请网站应用名称(二维码下方显示你的应用名),网站域名地址(扫完码,会跳转这个域名)(看看就好)
wx:
open:
# 微信开放平台 appid
app_id: wxed9954c01bb89b47
# 微信开放平台appsecret
app_secret: a7482517235173ddb4083788de60b90e
# 微信开放平台 重定向url(guli.shop需要在微信开放平台配置)
redirect_url: http://guli.shop/api/ucenter/wx/callback
3、官方文档
2、配置application.yml
wx:
open:
app_id: wxed9954c01bb89b47
app_secret: a7482517235173ddb4083788de60b90e
redirect_url: http://guli.shop/api/ucenter/wx/callback
3、配置类获取配置文件中的值
package com.yzpnb.ucenter_service.util;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class ConstantYmlUtil implements InitializingBean {
@Value("${wx.open.app_id}")
private String appId;
@Value("${wx.open.app_secret}")
private String appSecret;
@Value("${wx.open.redirect_url}")
private String redirectUrl;
public static String WX_OPEN_APP_ID;
public static String WX_OPEN_APP_SECRET;
public static String WX_OPEN_REDIRECT_URL;
@Override
public void afterPropertiesSet() throws Exception {
WX_OPEN_APP_ID = appId;
WX_OPEN_APP_SECRET = appSecret;
WX_OPEN_REDIRECT_URL = redirectUrl;
}
}
4、生成二维码
package com.yzpnb.ucenter_service.controller.api;
import com.yzpnb.service_base_handler.CustomExceptionHandler;
import com.yzpnb.ucenter_service.util.ConstantYmlUtil;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpSession;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
@CrossOrigin
@Controller
@RequestMapping("/ucenter_service/api/wx")
public class WxApiController {
@GetMapping("login")
public String genQrConnect(HttpSession session) {
String baseUrl = "https://open.weixin.qq.com/connect/qrconnect" +
"?appid=%s" +
"&redirect_uri=%s" +
"&response_type=code" +
"&scope=snsapi_login" +
"&state=%s" +
"#wechat_redirect";
String redirectUrl = ConstantYmlUtil.WX_OPEN_REDIRECT_URL;
try {
redirectUrl = URLEncoder.encode(redirectUrl, "UTF-8");
} catch (UnsupportedEncodingException e) {
throw new CustomExceptionHandler(20001, e.getMessage());
}
String state = "imhelen";
System.out.println("state = " + state);
String qrcodeUrl = String.format(
baseUrl,
ConstantYmlUtil.WX_OPEN_APP_ID,
redirectUrl,
state);
return "redirect:" + qrcodeUrl;
}
}
5、测试二维码
6、通过一些小手段,让我们扫描微信二维码后,跳转到我们本地(只是给我们这些没有公司的人用,比如和我一样还在上学的人,只能蹭人家的二维码)
尚硅谷的重定向接口地址:http://guli.shop/api/ucenter/wx/callback |
---|
它封装里一些重定向代码,当我们扫描二维码时,会重定向到 |
localhost:8150/api/ucenter/wx/callback?code=***** (省略内容) **** &state=******* |
1、把我们自己的端口号改为8150和重定向的端口一致 |
---|
2、将我们的请求地址和重定向一致,改为api/ucenter/wx |
---|
3、将我们的方法起名为和重定向地址相同的callback(代码还没写完,不需要照着写) |
---|
7、创建HTTPClient工具类(HttpClient是sun公司java原生技术,可以不用浏览器模拟http请求)
1、引入依赖
<!--httpClient-->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.12</version>
</dependency>
<!--commons-io-->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.7</version>
</dependency>
<!--json-->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.6</version>
</dependency>
2、工具类
package com.yzpnb.ucenter_service.util;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.http.Consts;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.config.RequestConfig.Builder;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContextBuilder;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.conn.ssl.X509HostnameVerifier;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocket;
import java.io.IOException;
import java.net.SocketTimeoutException;
import java.security.GeneralSecurityException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
public class HttpClientUtils {
public static final int connTimeout=10000;
public static final int readTimeout=10000;
public static final String charset="UTF-8";
private static HttpClient client = null;
static {
PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
cm.setMaxTotal(128);
cm.setDefaultMaxPerRoute(128);
client = HttpClients.custom().setConnectionManager(cm).build();
}
public static String postParameters(String url, String parameterStr) throws ConnectTimeoutException, SocketTimeoutException, Exception{
return post(url,parameterStr,"application/x-www-form-urlencoded",charset,connTimeout,readTimeout);
}
public static String postParameters(String url, String parameterStr,String charset, Integer connTimeout, Integer readTimeout) throws ConnectTimeoutException, SocketTimeoutException, Exception{
return post(url,parameterStr,"application/x-www-form-urlencoded",charset,connTimeout,readTimeout);
}
public static String postParameters(String url, Map<String, String> params) throws ConnectTimeoutException,
SocketTimeoutException, Exception {
return postForm(url, params, null, connTimeout, readTimeout);
}
public static String postParameters(String url, Map<String, String> params, Integer connTimeout,Integer readTimeout) throws ConnectTimeoutException,
SocketTimeoutException, Exception {
return postForm(url, params, null, connTimeout, readTimeout);
}
public static String get(String url) throws Exception {
return get(url, charset, null, null);
}
public static String get(String url, String charset) throws Exception {
return get(url, charset, connTimeout, readTimeout);
}
public static String post(String url, String body, String mimeType,String charset, Integer connTimeout, Integer readTimeout)
throws ConnectTimeoutException, SocketTimeoutException, Exception {
HttpClient client = null;
HttpPost post = new HttpPost(url);
String result = "";
try {
if (StringUtils.isNotBlank(body)) {
HttpEntity entity = new StringEntity(body, ContentType.create(mimeType, charset));
post.setEntity(entity);
}
Builder customReqConf = RequestConfig.custom();
if (connTimeout != null) {
customReqConf.setConnectTimeout(connTimeout);
}
if (readTimeout != null) {
customReqConf.setSocketTimeout(readTimeout);
}
post.setConfig(customReqConf.build());
HttpResponse res;
if (url.startsWith("https")) {
client = createSSLInsecureClient();
res = client.execute(post);
} else {
client = HttpClientUtils.client;
res = client.execute(post);
}
result = IOUtils.toString(res.getEntity().getContent(), charset);
} finally {
post.releaseConnection();
if (url.startsWith("https") && client != null&& client instanceof CloseableHttpClient) {
((CloseableHttpClient) client).close();
}
}
return result;
}
public static String postForm(String url, Map<String, String> params, Map<String, String> headers, Integer connTimeout,Integer readTimeout) throws ConnectTimeoutException,
SocketTimeoutException, Exception {
HttpClient client = null;
HttpPost post = new HttpPost(url);
try {
if (params != null && !params.isEmpty()) {
List<NameValuePair> formParams = new ArrayList<NameValuePair>();
Set<Entry<String, String>> entrySet = params.entrySet();
for (Entry<String, String> entry : entrySet) {
formParams.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
}
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(formParams, Consts.UTF_8);
post.setEntity(entity);
}
if (headers != null && !headers.isEmpty()) {
for (Entry<String, String> entry : headers.entrySet()) {
post.addHeader(entry.getKey(), entry.getValue());
}
}
Builder customReqConf = RequestConfig.custom();
if (connTimeout != null) {
customReqConf.setConnectTimeout(connTimeout);
}
if (readTimeout != null) {
customReqConf.setSocketTimeout(readTimeout);
}
post.setConfig(customReqConf.build());
HttpResponse res = null;
if (url.startsWith("https")) {
client = createSSLInsecureClient();
res = client.execute(post);
} else {
client = HttpClientUtils.client;
res = client.execute(post);
}
return IOUtils.toString(res.getEntity().getContent(), "UTF-8");
} finally {
post.releaseConnection();
if (url.startsWith("https") && client != null
&& client instanceof CloseableHttpClient) {
((CloseableHttpClient) client).close();
}
}
}
public static String get(String url, String charset, Integer connTimeout,Integer readTimeout)
throws ConnectTimeoutException,SocketTimeoutException, Exception {
HttpClient client = null;
HttpGet get = new HttpGet(url);
String result = "";
try {
Builder customReqConf = RequestConfig.custom();
if (connTimeout != null) {
customReqConf.setConnectTimeout(connTimeout);
}
if (readTimeout != null) {
customReqConf.setSocketTimeout(readTimeout);
}
get.setConfig(customReqConf.build());
HttpResponse res = null;
if (url.startsWith("https")) {
client = createSSLInsecureClient();
res = client.execute(get);
} else {
client = HttpClientUtils.client;
res = client.execute(get);
}
result = IOUtils.toString(res.getEntity().getContent(), charset);
} finally {
get.releaseConnection();
if (url.startsWith("https") && client != null && client instanceof CloseableHttpClient) {
((CloseableHttpClient) client).close();
}
}
return result;
}
@SuppressWarnings("unused")
private static String getCharsetFromResponse(HttpResponse ressponse) {
if (ressponse.getEntity() != null && ressponse.getEntity().getContentType() != null && ressponse.getEntity().getContentType().getValue() != null) {
String contentType = ressponse.getEntity().getContentType().getValue();
if (contentType.contains("charset=")) {
return contentType.substring(contentType.indexOf("charset=") + 8);
}
}
return null;
}
private static CloseableHttpClient createSSLInsecureClient() throws GeneralSecurityException {
try {
SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {
public boolean isTrusted(X509Certificate[] chain,String authType) throws CertificateException {
return true;
}
}).build();
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext, new X509HostnameVerifier() {
@Override
public boolean verify(String arg0, SSLSession arg1) {
return true;
}
@Override
public void verify(String host, SSLSocket ssl)
throws IOException {
}
@Override
public void verify(String host, X509Certificate cert)
throws SSLException {
}
@Override
public void verify(String host, String[] cns,
String[] subjectAlts) throws SSLException {
}
});
return HttpClients.custom().setSSLSocketFactory(sslsf).build();
} catch (GeneralSecurityException e) {
throw e;
}
}
public static void main(String[] args) {
try {
String str= post("https://localhost:443/ssl/test.shtml","name=12&page=34","application/x-www-form-urlencoded", "UTF-8", 10000, 10000);
System.out.println(str);
} catch (ConnectTimeoutException e) {
e.printStackTrace();
} catch (SocketTimeoutException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
}
8、controller层接口
@Autowired
UcenterMemberService ucenterMemberService;
@ApiOperation("获取扫描人信息,添加数据")
@GetMapping("callback")
public String callback(@ApiParam(name = "code",value = "code是微信官方api生成的唯一验证码(有过期时间),这个值可以通过请求固定地址" +
"获取访问凭证(access_token)和当前微信的唯一id值(openid)" +
"获取的凭证和id值可以让我们获取到微信扫码人的信息(头像昵称等)")String code,
@ApiParam(name = "state",value = "state是生成二维码的时候原样传递过来的") String state){
String baseAccessTokenUrl = "https://api.weixin.qq.com/sns/oauth2/access_token" +
"?appid=%s" +
"&secret=%s" +
"&code=%s" +
"&grant_type=authorization_code";
String accessTokenUrl = String.format(baseAccessTokenUrl,
ConstantYmlUtil.WX_OPEN_APP_ID,
ConstantYmlUtil.WX_OPEN_APP_SECRET,
code);
String result = null;
try {
result = HttpClientUtils.get(accessTokenUrl);
System.out.println("accessToken=============" + result);
} catch (Exception e) {
throw new CustomExceptionHandler(20001, "获取access_token失败");
}
Gson gson = new Gson();
HashMap map = gson.fromJson(result, HashMap.class);
String accessToken = (String)map.get("access_token");
String openid = (String)map.get("openid");
QueryWrapper<UcenterMember> queryWrapper =new QueryWrapper<>();
queryWrapper.eq("openid",openid);
UcenterMember ucenterMember = ucenterMemberService.getOne(queryWrapper);
if(ucenterMember == null){
System.out.println("新用户注册");
String baseUserInfoUrl = "https://api.weixin.qq.com/sns/userinfo" +
"?access_token=%s" +
"&openid=%s";
String userInfoUrl = String.format(baseUserInfoUrl, accessToken, openid);
String resultUserInfo = null;
try {
resultUserInfo = HttpClientUtils.get(userInfoUrl);
System.out.println("resultUserInfo==========" + resultUserInfo);
} catch (Exception e) {
throw new CustomExceptionHandler(20001, "获取用户信息失败");
}
HashMap<String, Object> mapUserInfo = gson.fromJson(resultUserInfo, HashMap.class);
String nickname = (String)mapUserInfo.get("nickname");
String headimgurl = (String)mapUserInfo.get("headimgurl");
ucenterMember = new UcenterMember();
ucenterMember.setNickname(nickname);
ucenterMember.setOpenid(openid);
ucenterMember.setAvatar(headimgurl);
ucenterMemberService.save(ucenterMember);
}
String token = JwtUtils.getJwtToken(ucenterMember.getId(),ucenterMember.getNickname());
return "redirect:http://localhost:3000?token=" + token;
}
十、前端整合测试
1、修改nginx(记得改完保存然后重启)
2、改代码
3、测试
vue-qriously