Browse Source

⬆️ 使用 justauth-spring-boot-starter 实现第三方登录,同时缓存使用自定义 redis 缓存 state

pull/1/head
Yangkai.Shen 5 years ago
parent
commit
ea383eeb36
7 changed files with 170 additions and 122 deletions
  1. +15
    -4
      spring-boot-demo-social/pom.xml
  2. +2
    -7
      spring-boot-demo-social/src/main/java/com/xkcoding/social/SpringBootDemoSocialApplication.java
  3. +39
    -0
      spring-boot-demo-social/src/main/java/com/xkcoding/social/config/justauth/JustAuthConfig.java
  4. +70
    -0
      spring-boot-demo-social/src/main/java/com/xkcoding/social/config/justauth/JustAuthRedisStateCache.java
  5. +18
    -57
      spring-boot-demo-social/src/main/java/com/xkcoding/social/controller/OauthController.java
  6. +0
    -54
      spring-boot-demo-social/src/main/java/com/xkcoding/social/props/OAuthProperties.java
  7. +26
    -0
      spring-boot-demo-social/src/main/resources/application.yml

+ 15
- 4
spring-boot-demo-social/pom.xml View File

@@ -20,7 +20,7 @@
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version> <java.version>1.8</java.version>
<spring.social.version>1.1.6.RELEASE</spring.social.version>
<justauth-spring-boot.version>1.0.0</justauth-spring-boot.version>
</properties> </properties>


<dependencies> <dependencies>
@@ -35,11 +35,22 @@
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>


<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

<!-- 对象池,使用redis时必须引入 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>

<!-- oauth工具类 --> <!-- oauth工具类 -->
<dependency> <dependency>
<groupId>me.zhyd.oauth</groupId>
<artifactId>JustAuth</artifactId>
<version>1.9.5</version>
<groupId>com.xkcoding</groupId>
<artifactId>justauth-spring-boot-starter</artifactId>
<version>${justauth-spring-boot.version}</version>
</dependency> </dependency>


<dependency> <dependency>


+ 2
- 7
spring-boot-demo-social/src/main/java/com/xkcoding/social/SpringBootDemoSocialApplication.java View File

@@ -8,13 +8,8 @@ import org.springframework.boot.autoconfigure.SpringBootApplication;
* 启动器 * 启动器
* </p> * </p>
* *
* @package: com.xkcoding.social
* @description: 启动器
* @author: yangkai.shen
* @date: Created in 2019-02-19 16:04
* @copyright: Copyright (c) 2019
* @version: V1.0
* @modified: yangkai.shen
* @author yangkai.shen
* @date Created in 2019-08-09 13:51
*/ */
@SpringBootApplication @SpringBootApplication
public class SpringBootDemoSocialApplication { public class SpringBootDemoSocialApplication {


+ 39
- 0
spring-boot-demo-social/src/main/java/com/xkcoding/social/config/justauth/JustAuthConfig.java View File

@@ -0,0 +1,39 @@
package com.xkcoding.social.config.justauth;

import me.zhyd.oauth.cache.AuthStateCache;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.io.Serializable;

/**
* <p>
* JustAuth自动装配
* </p>
*
* @author yangkai.shen
* @date Created in 2019-08-09 14:21
*/
@Configuration
public class JustAuthConfig {
/**
* 默认情况下的模板只能支持RedisTemplate<String, String>,也就是只能存入字符串,因此支持序列化
*/
@Bean
public RedisTemplate<String, Serializable> redisCacheTemplate(LettuceConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Serializable> template = new RedisTemplate<>();
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
template.setConnectionFactory(redisConnectionFactory);
return template;
}

@Bean
public AuthStateCache authStateCache(RedisTemplate<String, String> redisCacheTemplate) {
return new JustAuthRedisStateCache(redisCacheTemplate);
}
}

+ 70
- 0
spring-boot-demo-social/src/main/java/com/xkcoding/social/config/justauth/JustAuthRedisStateCache.java View File

@@ -0,0 +1,70 @@
package com.xkcoding.social.config.justauth;

import lombok.RequiredArgsConstructor;
import me.zhyd.oauth.cache.AuthStateCache;
import org.springframework.data.redis.core.RedisTemplate;

import java.util.concurrent.TimeUnit;

/**
* <p>
* Redis作为JustAuth的State的缓存
* </p>
*
* @author yangkai.shen
* @date Created in 2019-08-09 14:22
*/
@RequiredArgsConstructor
public class JustAuthRedisStateCache implements AuthStateCache {
private final RedisTemplate<String, String> redisTemplate;
private static final long DEF_TIMEOUT = 3 * 60 * 1000;

/**
* 存入缓存
*
* @param key 缓存key
* @param value 缓存内容
*/
@Override
public void cache(String key, String value) {
this.cache(key, value, DEF_TIMEOUT);
}

/**
* 存入缓存
*
* @param key 缓存key
* @param value 缓存内容
* @param timeout 指定缓存过期时间(毫秒)
*/
@Override
public void cache(String key, String value, long timeout) {
redisTemplate.opsForValue().set(key, value, timeout, TimeUnit.MILLISECONDS);
}

/**
* 获取缓存内容
*
* @param key 缓存key
* @return 缓存内容
*/
@Override
public String get(String key) {
return redisTemplate.opsForValue().get(key);
}

/**
* 是否存在key,如果对应key的value值已过期,也返回false
*
* @param key 缓存key
* @return true:存在key,并且value没过期;false:key不存在或者已过期
*/
@Override
public boolean containsKey(String key) {
Long expire = redisTemplate.getExpire(key, TimeUnit.MILLISECONDS);
if (expire == null) {
expire = 0L;
}
return expire > 0;
}
}

+ 18
- 57
spring-boot-demo-social/src/main/java/com/xkcoding/social/controller/OauthController.java View File

@@ -1,15 +1,14 @@
package com.xkcoding.social.controller; package com.xkcoding.social.controller;


import cn.hutool.core.lang.Dict;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil; import cn.hutool.json.JSONUtil;
import com.xkcoding.social.props.OAuthProperties;
import com.xkcoding.justauth.AuthRequestFactory;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import me.zhyd.oauth.config.AuthConfig;
import me.zhyd.oauth.config.AuthSource; import me.zhyd.oauth.config.AuthSource;
import me.zhyd.oauth.model.AuthCallback; import me.zhyd.oauth.model.AuthCallback;
import me.zhyd.oauth.model.AuthResponse; import me.zhyd.oauth.model.AuthResponse;
import me.zhyd.oauth.request.*;
import me.zhyd.oauth.request.AuthRequest;
import me.zhyd.oauth.utils.AuthStateUtils; import me.zhyd.oauth.utils.AuthStateUtils;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
@@ -19,6 +18,9 @@ import org.springframework.web.bind.annotation.RestController;


import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import java.io.IOException; import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;


/** /**
* <p> * <p>
@@ -38,14 +40,15 @@ import java.io.IOException;
@RequestMapping("/oauth") @RequestMapping("/oauth")
@RequiredArgsConstructor(onConstructor_ = @Autowired) @RequiredArgsConstructor(onConstructor_ = @Autowired)
public class OauthController { public class OauthController {
private final OAuthProperties properties;
private final AuthRequestFactory factory;


/** /**
* 登录类型 * 登录类型
*/ */
@GetMapping @GetMapping
public Dict loginType() {
return Dict.create().set("QQ登录", "http://oauth.xkcoding.com/demo/oauth/login/qq").set("GitHub登录", "http://oauth.xkcoding.com/demo/oauth/login/github").set("微信登录", "http://oauth.xkcoding.com/demo/oauth/login/wechat").set("Google登录", "http://oauth.xkcoding.com/demo/oauth/login/google").set("Microsoft 登录", "http://oauth.xkcoding.com/demo/oauth/login/microsoft").set("小米登录", "http://oauth.xkcoding.com/demo/oauth/login/mi");
public Map<String, String> loginType() {
List<String> oauthList = factory.oauthList();
return oauthList.stream().collect(Collectors.toMap(oauth -> oauth.toLowerCase() + "登录", oauth -> "http://oauth.xkcoding.com/demo/oauth/login/" + oauth.toLowerCase()));
} }


/** /**
@@ -57,8 +60,8 @@ public class OauthController {
*/ */
@RequestMapping("/login/{oauthType}") @RequestMapping("/login/{oauthType}")
public void renderAuth(@PathVariable String oauthType, HttpServletResponse response) throws IOException { public void renderAuth(@PathVariable String oauthType, HttpServletResponse response) throws IOException {
AuthRequest authRequest = getAuthRequest(oauthType);
response.sendRedirect(authRequest.authorize(AuthStateUtils.createState()));
AuthRequest authRequest = factory.get(getAuthSource(oauthType));
response.sendRedirect(authRequest.authorize(oauthType + "::" + AuthStateUtils.createState()));
} }


/** /**
@@ -70,59 +73,17 @@ public class OauthController {
*/ */
@RequestMapping("/{oauthType}/callback") @RequestMapping("/{oauthType}/callback")
public AuthResponse login(@PathVariable String oauthType, AuthCallback callback) { public AuthResponse login(@PathVariable String oauthType, AuthCallback callback) {
AuthRequest authRequest = getAuthRequest(oauthType);
AuthRequest authRequest = factory.get(getAuthSource(oauthType));
AuthResponse response = authRequest.login(callback); AuthResponse response = authRequest.login(callback);
log.info("【response】= {}", JSONUtil.toJsonStr(response)); log.info("【response】= {}", JSONUtil.toJsonStr(response));
return response; return response;
} }


private AuthRequest getAuthRequest(String oauthType) {
AuthSource authSource = AuthSource.valueOf(oauthType.toUpperCase());
switch (authSource) {
case QQ:
return getQqAuthRequest();
case GITHUB:
return getGithubAuthRequest();
case WECHAT:
return getWechatAuthRequest();
case GOOGLE:
return getGoogleAuthRequest();
case MICROSOFT:
return getMicrosoftAuthRequest();
case MI:
return getMiAuthRequest();
default:
throw new RuntimeException("暂不支持的第三方登录");
private AuthSource getAuthSource(String type) {
if (StrUtil.isNotBlank(type)) {
return AuthSource.valueOf(type.toUpperCase());
} else {
throw new RuntimeException("不支持的类型");
} }
} }

private AuthRequest getQqAuthRequest() {
AuthConfig authConfig = properties.getQq();
return new AuthQqRequest(authConfig);
}

private AuthRequest getGithubAuthRequest() {
AuthConfig authConfig = properties.getGithub();
return new AuthGithubRequest(authConfig);
}

private AuthRequest getWechatAuthRequest() {
AuthConfig authConfig = properties.getWechat();
return new AuthWeChatRequest(authConfig);
}

private AuthRequest getGoogleAuthRequest() {
AuthConfig authConfig = properties.getGoogle();
return new AuthGoogleRequest(authConfig);
}

private AuthRequest getMicrosoftAuthRequest() {
AuthConfig authConfig = properties.getMicrosoft();
return new AuthMicrosoftRequest(authConfig);
}

private AuthRequest getMiAuthRequest() {
AuthConfig authConfig = properties.getMi();
return new AuthMiRequest(authConfig);
}
} }

+ 0
- 54
spring-boot-demo-social/src/main/java/com/xkcoding/social/props/OAuthProperties.java View File

@@ -1,54 +0,0 @@
package com.xkcoding.social.props;

import lombok.Data;
import me.zhyd.oauth.config.AuthConfig;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

/**
* <p>
* 第三方登录配置
* </p>
*
* @package: com.xkcoding.oauth.config.props
* @description: 第三方登录配置
* @author: yangkai.shen
* @date: Created in 2019-05-17 15:33
* @copyright: Copyright (c) 2019
* @version: V1.0
* @modified: yangkai.shen
*/
@Data
@Component
@ConfigurationProperties(prefix = "oauth")
public class OAuthProperties {
/**
* QQ 配置
*/
private AuthConfig qq;

/**
* github 配置
*/
private AuthConfig github;

/**
* 微信 配置
*/
private AuthConfig wechat;

/**
* Google 配置
*/
private AuthConfig google;

/**
* Microsoft 配置
*/
private AuthConfig microsoft;

/**
* Mi 配置
*/
private AuthConfig mi;
}

+ 26
- 0
spring-boot-demo-social/src/main/resources/application.yml View File

@@ -3,6 +3,27 @@ server:
servlet: servlet:
context-path: /demo context-path: /demo


spring:
redis:
host: localhost
# 连接超时时间(记得添加单位,Duration)
timeout: 10000ms
# Redis默认情况下有16个分片,这里配置具体使用的分片
# database: 0
lettuce:
pool:
# 连接池最大连接数(使用负值表示没有限制) 默认 8
max-active: 8
# 连接池最大阻塞等待时间(使用负值表示没有限制) 默认 -1
max-wait: -1ms
# 连接池中的最大空闲连接 默认 8
max-idle: 8
# 连接池中的最小空闲连接 默认 0
min-idle: 0
cache:
# 一般来说是不用配置的,Spring Cache 会根据依赖的包自行装配
type: redis

oauth: oauth:
qq: qq:
client-id: 1015***** client-id: 1015*****
@@ -28,3 +49,8 @@ oauth:
client-id: 2882303************** client-id: 2882303**************
client-secret: nFeTt89Yn************** client-secret: nFeTt89Yn**************
redirect-uri: http://oauth.xkcoding.com/demo/oauth/mi/callback redirect-uri: http://oauth.xkcoding.com/demo/oauth/mi/callback
wechat_enterprise:
client-id: ww58**********6fbc
client-secret: 8G6PCr0****************************yzaPc78
redirect-uri: http://oauth.xkcoding.com/demo/oauth/wechat_enterprise/callback
agent-id: 10*******02

Loading…
Cancel
Save