当前位置:   article > 正文

SpringBoot整合OAuth2和Jwt实现第三方登录_springboot oauth2 jwt

springboot oauth2 jwt

SpringBoot整合OAuth2和Jwt实现第三方登录

一、OAuth2的使用场景
  • 现代微服务中系统微服务化以及应用的形态和设备类型增多,不能用传统的登录方式
  • 核心的技术不是用户名和密码,而是token,由AuthServer颁发token,用户使用token进行登录

在这里插入图片描述

二、OAuth详解
2.1 什么是OAuth

在这里插入图片描述

2.2 OAuth的优势

在这里插入图片描述

2.3 OAuth术语

在这里插入图片描述在这里插入图片描述

2.4 OAuth2令牌的类型

在这里插入图片描述

三、和SpringBoot整合
3.1 准备工作
  1. 首先注册微信开放平台账号 微信开放平台
  2. 邮箱激活
  3. 完善开发者资料
  4. 开发者资质认证
    4.1 准备营业执照,1-2个工作日审批、300元
  5. 创建网站应用
    5.1 提交审核,7个工作日审批
  6. 熟悉微信登录流程 参考文档
3.2 请求微信登录流程图

微信登录请求流程图

3.3 添加配置文件
# outh2相关配置
wx:
  open:
    #微信开放平台 appid
    app_id: xxxxxxxxxxxxxxx
    # 微信开放平台 appsecret
    app_secret: xxxxxxxxxxxxxxxxx
    # 微信开放平台 重定向url(guli.shop需要在微信开放平台配置)
    redirect_url: http://xxxxxxxxxxx

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
3.4 将相关信息做成配置类
@Component
//@PropertySource("classpath:application.yml")
public class ConstantPropertiesUtil 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;
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
3.5 定义请求登录方法
	@GetMapping("/ucenterService/login")
	@ApiOperation("生成微信扫描二维码")
	public String genQrConnect(HttpSession session) {
		LOGGER.info("生成微信扫描二维码  start ");
	/*	固定地址,后面拼接参数
       String url = "https://open.weixin.qq.com/" +
                "connect/qrconnect?appid="+ ConstantWxUtils.WX_OPEN_APP_ID+"&response_type=code";*/


		// 微信开放平台授权baseUrl %s相当于?代表占位符(此地址为固定地址,固定参数 )
		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 = ConstantWxUtils.WX_OPEN_REDIRECT_URL;
		try {
			//对redirect_url进行URLEncoder编码
			redirectUrl = URLEncoder.encode(redirectUrl, "UTF-8");
		} catch (UnsupportedEncodingException e) {
			throw new RuntimeException(20001, e.getMessage());
		}

		// 防止csrf攻击(跨站请求伪造攻击)
		//String state = UUID.randomUUID().toString().replaceAll("-", "");//一般情况下会使用一个随机数

		//  这里填写你在ngrok的前置域名
		String state = "xxxx";
		LOGGER.info("state = {}", state);

		// 采用redis等进行缓存state 使用sessionId为key 30分钟后过期,可配置
		//键:"wechar-open-state-" + httpServletRequest.getSession().getId()
		//值:satte
		//过期时间:30分钟

		// 生成qrcodeUrl
		String qrcodeUrl = String.format(
				baseUrl,
				ConstantWxUtils.WX_OPEN_APP_ID,
				redirectUrl,
				state
		);

		LOGGER.info("生成微信扫描二维码  end 重定向地址为:{}", qrcodeUrl);
		return "redirect:" + qrcodeUrl;
	}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
参数是否必须说明
appid应用唯一标识
redirect_uri请使用urlEncode对链接进行处理
response_type填code
scope应用授权作用域,拥有多个作用域用逗号(,)分隔,网页应用目前仅填写snsapi_login即
state用于保持请求和回调的状态,授权请求后原样带回给第三方。该参数可用于防止csrf攻击(跨站请求伪造攻击),建议第三方带上该参数,可设置为简单的随机数加session进行校验

请求登录的流程大致是这样子的:

  1. 首先请求一个固定的地址(该地址由微信官方提供,我们只需要按照要求提供相应的参数来调用即可)
  2. 为了安全性,redirect_url需要进行URLEncoder编码
  3. 请求完成后,微信会返回给我们一个地址,我们只需要重定向到该地址,就可以在该页面看到相应的二维码信息。
  4. 在我们扫码点击登录后,微信官方就会将我们的请求转发到我们的回调方法上(需要自己配置),我们在回调方法上来进行一些数据的操作。
3.6 回调方法
	@GetMapping("callback")
	@ApiOperation(value = "获取扫描人信息,添加数据", tags = "当用户扫描二维码登录后,会回调到本方法中")
	public String callback(String code, String state, HttpSession session) {
		LOGGER.info("用户微信扫描登陆之后开始进行回调: code = {},state = {}", code, state);

		// 从redis中将state获取出来,和当前传入的state作比较(正确的做法)
		// 如果一致则放行,如果不一致则抛出异常:非法访问(正确的做法)

		try {
			// 1 获取code值,临时票据,类似于验证码
			// 2 拿着code请求 微信固定的地址,得到两个值 accsess_token 和 openid
			// 向认证服务器发送请求换取access_token
			String baseAccessTokenUrl = "https://api.weixin.qq.com/sns/oauth2/access_token" +
					"?appid=%s" +
					"&secret=%s" +
					"&code=%s" +
					"&grant_type=authorization_code";

			// 拼接三个参数 :id  秘钥 和 code值
			String accessTokenUrl = String.format(baseAccessTokenUrl,
					ConstantWxUtils.WX_OPEN_APP_ID,
					ConstantWxUtils.WX_OPEN_APP_SECRET,
					code);

			// 请求这个拼接好的地址,得到返回两个值 accsess_token 和 openid
			// 使用httpclient发送请求,得到返回结果
			String accessTokenInfo = HttpClientUtils.get(accessTokenUrl);
			LOGGER.info("accessTokenInfo = {}", accessTokenInfo);
			// 从accessTokenInfo字符串获取出来两个值 accsess_token 和 openid
			// 把accessTokenInfo字符串转换map集合,根据map里面key获取对应值
			// 使用json转换工具 Gson
			Gson gson = new Gson();
			HashMap mapAccessToken = gson.fromJson(accessTokenInfo, HashMap.class);
			String accsess_token = (String) mapAccessToken.get("access_token");
			String openid = (String) mapAccessToken.get("openid");

			LOGGER.info("成功得到accsess_token和openid  accsess_token = {},openid = ", accsess_token, openid);
			// 把扫描人信息添加数据库里面
			// 判断数据表里面是否存在相同微信信息,根据openid判断(openid是唯一的)
			xXXXX   XXX  = xxxxService.getOpenId(openid);

			// 之前没有用微信登陆过
			if (null == XXXX) {
				LOGGER.info("当前用户尚未使用过微信登录,将会该用户的信息存入数据库");
				// 3 拿着得到accsess_token 和 openid,再去请求微信提供固定的地址,获取到扫描人信息
				// 访问微信的资源服务器,获取用户信息
				String baseUserInfoUrl = "https://api.weixin.qq.com/sns/userinfo" +
						"?access_token=%s" +
						"&openid=%s";
				//拼接两个参数
				String userInfoUrl = String.format(
						baseUserInfoUrl,
						accsess_token,
						openid
				);
				// 发送请求,得到微信官方返回的用户信息
				String userInfo = HttpClientUtils.get(userInfoUrl);
				LOGGER.info("微信官方返回的用户信息为:{}", userInfo);
				//获取返回userinfo字符串扫描人信息
				HashMap userInfoMap = gson.fromJson(userInfo, HashMap.class);
				String nickname = (String) userInfoMap.get("nickname");//昵称
				String headimgurl = (String) userInfoMap.get("headimgurl");//头像



				//将用户的头像信息和昵称存入数据库  略
				




				LOGGER.info("成功将微信信息存入数据库中,存入的信息为:{}", member);
			}
			LOGGER.info("当前用户已经使用过微信进行登录,直接返回该用户的信息");
			// TODO 登录

			// 生成jwt
			String token = JwtUtils.getJwtToken(xxx.getId(), xxx.getNickname());

			// 存入cookie
			//CookieUtils.setCookie(request, response, "guli_jwt_token", token);

			//  因为端口号不同存在跨域问题,cookie不能跨域,所以这里使用url重写

			LOGGER.info("微信登陆成功后重定向到首页,携带的token信息为:{}", token );
			return "redirect:http://xxx:xxxx?token=" + token;

		} catch (Exception e) {
			LOGGER.error("微信登录失败,异常信息为:{}", e.toString());
			throw new RunTimeException(20001, "登录失败");
		}
	}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92

回调方法简介

  1. 首先我们可以直接在请求参数中添加code 和 state,这两个信息就类似于验证码,我们在通过这两个数据去请求微信固定的地址,得到两个值accsess_token 和 openid 微信返回的数据为JSON格式,我们可以通过fastJson或其他来将数据转换为Map集合,在通过map集合来取到其中的这两个数据。
  2. 然后在通过accsess_token 和 openid这两个数据请求另一个微信官方的地址来获取微信账号的信息(包括微信账号的头像地址和微信名称)
  3. 获取到这些信息之后,便可一通过openid(唯一标识)进行判断,来判断用户之前是否使用过微信登录,如果没有登陆过,就可以将用户信息保存到数据库中。
  4. 使用JWT公具类来根据用户的openid和微信昵称来生成token
  5. 微信登陆成功后重定向到到目标页面,并且携带token信息
  6. 前端页面从请求路径中达到token信息,将token存储在cookie或者请头头中。
  7. 用户携带token请求用户信息,后端解析通过,将用户信息返回给前端,前端将用户信息作展示。
3.7 前端页面拦截请求路径的token并存储在cookie中
export default {
  data() {
    return {
      token: '',
      	   }
    },
     created() {
    this.token = this.$route.query.token
    if (this.token) {
      this.wxLogin()
    }
  }, 
methods: {
    wxLogin() {
      if (this.token == '') return
      //把token存在cookie中、也可以放在localStorage中
      cookie.set('guli_token', this.token, {domain: 'localhost'})
      cookie.set('guli_ucenter', '', {domain: 'localhost'})
      //登录成功根据token获取用户信息
      userApi.getLoginInfo().then(response => {
        this.loginInfo = response.data.data.item
        //将用户信息记录cookie
        cookie.set('guli_ucenter', this.loginInfo, {domain: 'localhost'})
      })
    }
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

其中涉及到的jwt工具类和HttpClientUtils可以私聊我,我私发给你

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/727355
推荐阅读
相关标签
  

闽ICP备14008679号