├── commons ├── src │ └── main │ │ ├── resources │ │ └── META-INF │ │ │ ├── spring.factories │ │ │ └── spring-configuration-metadata.json │ │ └── java │ │ └── com │ │ └── lyf │ │ └── springboot │ │ └── wx │ │ ├── auto │ │ ├── WxAutoConfiguration.java │ │ └── WeChatProperties.java │ │ ├── WxAuthServiceBase.java │ │ ├── data │ │ ├── WeChatAuthResponse.java │ │ └── WeChatUserInfo.java │ │ └── utils │ │ ├── WxConstants.java │ │ └── JsonHelper.java └── pom.xml ├── starter-feign ├── src │ ├── main │ │ ├── resources │ │ │ └── META-INF │ │ │ │ └── spring.factories │ │ └── java │ │ │ └── com │ │ │ └── lyf │ │ │ └── springboot │ │ │ └── wechat │ │ │ ├── WxFeignConfiguration.java │ │ │ ├── ClientAuthService.java │ │ │ └── WxAuthClient.java │ └── test │ │ └── java │ │ └── com │ │ └── lyf │ │ └── springboot │ │ └── wechat │ │ └── StarterFeignApplicationTests.java └── pom.xml ├── starter-httpclient ├── src │ └── main │ │ ├── resources │ │ └── META-INF │ │ │ └── spring.factories │ │ └── java │ │ └── com │ │ └── lyf │ │ └── springboot │ │ └── wx │ │ ├── auto │ │ └── WxHttpClientAutoConfiguration.java │ │ ├── service │ │ └── ClientAuthService.java │ │ └── utils │ │ └── HttpClientUtil.java └── pom.xml ├── demo ├── src │ └── main │ │ ├── resources │ │ └── application.yml │ │ └── java │ │ └── com │ │ └── lyf │ │ └── springboot │ │ └── wx │ │ └── demo │ │ ├── SessionConstants.java │ │ ├── WxDemoApplication.java │ │ ├── DemoService.java │ │ ├── WxAuthController.java │ │ └── LoginInterceptor.java └── pom.xml ├── .gitignore ├── pom.xml └── README.md /commons/src/main/resources/META-INF/spring.factories: -------------------------------------------------------------------------------- 1 | org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ 2 | com.lyf.springboot.wx.auto.WxAutoConfiguration 3 | -------------------------------------------------------------------------------- /starter-feign/src/main/resources/META-INF/spring.factories: -------------------------------------------------------------------------------- 1 | org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ 2 | com.lyf.springboot.wechat.WxFeignConfiguration 3 | -------------------------------------------------------------------------------- /starter-httpclient/src/main/resources/META-INF/spring.factories: -------------------------------------------------------------------------------- 1 | org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ 2 | com.lyf.springboot.wx.auto.WxHttpClientAutoConfiguration 3 | -------------------------------------------------------------------------------- /demo/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | social: 3 | wx: 4 | app-id: 1 # 配置自己申请的appId 5 | app-secret: 2 #配置为自己申请的appSecret 6 | app-name: 3 #此处是为了区分多个app进行的配置 可选 7 | device-type: 2 -------------------------------------------------------------------------------- /demo/src/main/java/com/lyf/springboot/wx/demo/SessionConstants.java: -------------------------------------------------------------------------------- 1 | package com.lyf.springboot.wx.demo; 2 | 3 | public class SessionConstants { 4 | 5 | public static final String SESSION_AGENT_WXUSER = "sessionAgentWxUser"; 6 | 7 | public static final String AGENT_WX_ID = "agent_sid"; 8 | 9 | 10 | public static final String AUTH_REDIRECT = "redirectUrl"; 11 | } 12 | -------------------------------------------------------------------------------- /starter-feign/src/test/java/com/lyf/springboot/wechat/StarterFeignApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.lyf.springboot.wechat; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class StarterFeignApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | 4 | ### STS ### 5 | .apt_generated 6 | .classpath 7 | .factorypath 8 | .project 9 | .settings 10 | .springBeans 11 | 12 | ### IntelliJ IDEA ### 13 | .idea 14 | *.iws 15 | *.iml 16 | */*.iml 17 | *.ipr 18 | 19 | ### NetBeans ### 20 | nbproject/private/ 21 | build/ 22 | nbbuild/ 23 | dist/ 24 | nbdist/ 25 | .nb-gradle/ 26 | .idea -------------------------------------------------------------------------------- /commons/src/main/java/com/lyf/springboot/wx/auto/WxAutoConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.lyf.springboot.wx.auto; 2 | 3 | import org.springframework.boot.context.properties.EnableConfigurationProperties; 4 | import org.springframework.context.annotation.Configuration; 5 | 6 | /** 7 | * 8 | * @author 永锋 9 | * @date 2017/9/28 10 | */ 11 | @Configuration 12 | @EnableConfigurationProperties(WeChatProperties.class) 13 | public class WxAutoConfiguration { 14 | 15 | } 16 | -------------------------------------------------------------------------------- /demo/src/main/java/com/lyf/springboot/wx/demo/WxDemoApplication.java: -------------------------------------------------------------------------------- 1 | package com.lyf.springboot.wx.demo; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.context.annotation.ComponentScan; 6 | 7 | /** 8 | * Created by 永锋 on 2017/9/28. 9 | */ 10 | @SpringBootApplication 11 | @ComponentScan(basePackages = {"com.lyf.springboot"}) 12 | public class WxDemoApplication { 13 | public static void main(String[] args) { 14 | SpringApplication.run(WxDemoApplication.class, args); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /commons/src/main/resources/META-INF/spring-configuration-metadata.json: -------------------------------------------------------------------------------- 1 | { 2 | "properties": [ 3 | { 4 | "name": "spring.social.wx.app-id", 5 | "type": "java.lang.String", 6 | "sourceType": "com.lyf.springboot.wx.auto.WeChatProperties" 7 | }, 8 | { 9 | "name": "spring.social.wx.app-secret", 10 | "type": "java.lang.String", 11 | "sourceType": "com.lyf.springboot.wx.auto.WeChatProperties" 12 | }, 13 | { 14 | "name": "spring.social.wx.app-name", 15 | "type": "java.lang.String", 16 | "sourceType": "com.lyf.springboot.wx.auto.WeChatProperties" 17 | }, 18 | { 19 | "name": "spring.social.wx.device-type", 20 | "type": "java.lang.String", 21 | "description": "wx platform type, 1 for app, 2 for web", 22 | "defaultValue": 1, 23 | "sourceType": "com.lyf.springboot.wx.auto.WeChatProperties" 24 | } 25 | ], 26 | "hints": [] 27 | } -------------------------------------------------------------------------------- /demo/src/main/java/com/lyf/springboot/wx/demo/DemoService.java: -------------------------------------------------------------------------------- 1 | package com.lyf.springboot.wx.demo; 2 | 3 | import com.lyf.springboot.wx.WxAuthServiceBase; 4 | import com.lyf.springboot.wx.data.WeChatAuthResponse; 5 | import com.lyf.springboot.wx.data.WeChatUserInfo; 6 | import org.springframework.stereotype.Service; 7 | 8 | /** 9 | * DemoService 10 | * 11 | * @author yongfeng.liu 12 | */ 13 | @Service 14 | public class DemoService { 15 | private final WxAuthServiceBase wxAuthService; 16 | 17 | public DemoService(WxAuthServiceBase wxAuthService) { 18 | this.wxAuthService = wxAuthService; 19 | } 20 | 21 | public WeChatUserInfo authAndUser(String code){ 22 | WeChatAuthResponse authResponse = wxAuthService.auth(code); 23 | if(authResponse == null) { 24 | return null; 25 | } 26 | return wxAuthService.userInfo(authResponse.getAccessToken(), authResponse.getOpenid()); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /commons/src/main/java/com/lyf/springboot/wx/WxAuthServiceBase.java: -------------------------------------------------------------------------------- 1 | package com.lyf.springboot.wx; 2 | 3 | import com.lyf.springboot.wx.auto.WeChatProperties; 4 | import com.lyf.springboot.wx.data.WeChatAuthResponse; 5 | import com.lyf.springboot.wx.data.WeChatUserInfo; 6 | 7 | /** 8 | * Created by 永锋 on 2017/9/28. 9 | */ 10 | public abstract class WxAuthServiceBase { 11 | protected WeChatProperties weChatProperties; 12 | 13 | public WxAuthServiceBase(WeChatProperties weChatProperties) { 14 | this.weChatProperties = weChatProperties; 15 | } 16 | 17 | /** 18 | * 微信授权 19 | * 20 | * @param authorizationCode 21 | * @return 22 | */ 23 | public abstract WeChatAuthResponse auth(String authorizationCode); 24 | 25 | /** 26 | * 获取用户的微信个人信息 27 | * 28 | * @param accessToken 29 | * @param openId 30 | * @return 31 | */ 32 | public abstract WeChatUserInfo userInfo(String accessToken, String openId); 33 | 34 | } 35 | -------------------------------------------------------------------------------- /commons/src/main/java/com/lyf/springboot/wx/data/WeChatAuthResponse.java: -------------------------------------------------------------------------------- 1 | package com.lyf.springboot.wx.data; 2 | 3 | 4 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 5 | import com.fasterxml.jackson.databind.PropertyNamingStrategy; 6 | import com.fasterxml.jackson.databind.annotation.JsonNaming; 7 | import lombok.Data; 8 | 9 | /** 10 | * right result { "access_token":"ACCESS_TOKEN", "expires_in":7200, "refresh_token":"REFRESH_TOKEN", 11 | * "openid":"OPENID", "scope":"SCOPE", "unionid":"o6_bmasdasdsad6_2sgVt1231234" } 12 | */ 13 | @JsonIgnoreProperties(ignoreUnknown = true) 14 | @JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class) 15 | @Data 16 | public class WeChatAuthResponse { 17 | 18 | private int errcode; 19 | 20 | private String errmsg; 21 | 22 | private String accessToken; 23 | 24 | private int expiresIn; 25 | 26 | private String refreshToken; 27 | 28 | private String openid; 29 | 30 | private String scope; 31 | 32 | private String unionid; 33 | 34 | } 35 | -------------------------------------------------------------------------------- /starter-httpclient/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | wechat-parent 7 | com.lyf.springboot.wechat 8 | 0.0.2-SNAPSHOT 9 | ../pom.xml 10 | 11 | 4.0.0 12 | 13 | starter-httpclient 14 | 15 | 16 | 17 | org.apache.httpcomponents 18 | httpclient 19 | 20 | 21 | 22 | ${project.groupId} 23 | commons 24 | ${project.version} 25 | 26 | 27 | org.projectlombok 28 | lombok 29 | provided 30 | 31 | 32 | -------------------------------------------------------------------------------- /starter-httpclient/src/main/java/com/lyf/springboot/wx/auto/WxHttpClientAutoConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.lyf.springboot.wx.auto; 2 | 3 | import com.lyf.springboot.wx.WxAuthServiceBase; 4 | import com.lyf.springboot.wx.service.ClientAuthService; 5 | import org.springframework.boot.autoconfigure.AutoConfigureAfter; 6 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 7 | import org.springframework.boot.context.properties.ConfigurationProperties; 8 | import org.springframework.boot.context.properties.EnableConfigurationProperties; 9 | import org.springframework.context.annotation.Bean; 10 | import org.springframework.context.annotation.Configuration; 11 | 12 | /** 13 | * 14 | * @author 永锋 15 | * @date 2017/9/28 16 | */ 17 | @Configuration 18 | @AutoConfigureAfter(WxAutoConfiguration.class) 19 | public class WxHttpClientAutoConfiguration { 20 | @Bean(name = "wxAuthService") 21 | @ConditionalOnMissingBean(WxAuthServiceBase.class) 22 | public WxAuthServiceBase wxAuthService(WeChatProperties weChatProperties){ 23 | return new ClientAuthService(weChatProperties); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /starter-feign/src/main/java/com/lyf/springboot/wechat/WxFeignConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.lyf.springboot.wechat; 2 | 3 | import com.lyf.springboot.wx.WxAuthServiceBase; 4 | import com.lyf.springboot.wx.auto.WeChatProperties; 5 | import com.lyf.springboot.wx.auto.WxAutoConfiguration; 6 | import org.springframework.boot.autoconfigure.AutoConfigureAfter; 7 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 8 | import org.springframework.cloud.openfeign.EnableFeignClients; 9 | import org.springframework.context.annotation.Bean; 10 | import org.springframework.context.annotation.Configuration; 11 | 12 | /** 13 | * 使用feign client 访问微信 14 | * 15 | * @author yongfeng.liu 16 | */ 17 | @Configuration 18 | @EnableFeignClients(clients = {WxAuthClient.class}) 19 | @AutoConfigureAfter(WxAutoConfiguration.class) 20 | public class WxFeignConfiguration { 21 | 22 | @Bean 23 | @ConditionalOnMissingBean(WxAuthServiceBase.class) 24 | public WxAuthServiceBase wxAuthService(WeChatProperties chatProperties, WxAuthClient wxAuthClient) { 25 | return new ClientAuthService(chatProperties, wxAuthClient); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /starter-feign/src/main/java/com/lyf/springboot/wechat/ClientAuthService.java: -------------------------------------------------------------------------------- 1 | package com.lyf.springboot.wechat; 2 | 3 | import com.lyf.springboot.wx.WxAuthServiceBase; 4 | import com.lyf.springboot.wx.auto.WeChatProperties; 5 | import com.lyf.springboot.wx.data.WeChatAuthResponse; 6 | import com.lyf.springboot.wx.data.WeChatUserInfo; 7 | 8 | /** 9 | * @author 10 | */ 11 | public class ClientAuthService extends WxAuthServiceBase { 12 | private WxAuthClient wxAuthClient; 13 | 14 | public ClientAuthService(WeChatProperties weChatProperties, WxAuthClient wxAuthClient) { 15 | super(weChatProperties); 16 | this.wxAuthClient = wxAuthClient; 17 | } 18 | 19 | @Override 20 | public WeChatAuthResponse auth(String authorizationCode) { 21 | return wxAuthClient.auth(this.weChatProperties.getAppId(), this.weChatProperties.getAppSecret(), 22 | authorizationCode, WxAuthClient.GRANT_TYPE_AUTH); 23 | } 24 | 25 | @Override 26 | public WeChatUserInfo userInfo(String accessToken, String openId) { 27 | return wxAuthClient.userInfo(accessToken, openId, WxAuthClient.LANG_CHINA); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /commons/src/main/java/com/lyf/springboot/wx/utils/WxConstants.java: -------------------------------------------------------------------------------- 1 | package com.lyf.springboot.wx.utils; 2 | 3 | import java.io.UnsupportedEncodingException; 4 | import java.net.URLEncoder; 5 | import java.nio.charset.StandardCharsets; 6 | 7 | /** 8 | * 9 | * @author 永锋 10 | * @date 2016/11/23 11 | */ 12 | public interface WxConstants { 13 | String API_HOST = "https://api.weixin.qq.com"; 14 | String AUTH_PATH = "/sns/oauth2/access_token"; 15 | String USER_INFO_PATH = "/sns/userinfo"; 16 | 17 | String MEDIA_DOWN_URL = "https://api.weixin.qq.com/cgi-bin/media/get?access_token=%s&media_id=%s"; 18 | 19 | String WX_LOGIN_URL = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=%s" 20 | + "&redirect_uri=%s&response_type=code&scope=snsapi_userinfo"; 21 | 22 | 23 | /** 24 | * 微信授权链接 25 | * @param appId 26 | * @param redirectUrl 27 | * @return 28 | * @throws UnsupportedEncodingException 29 | */ 30 | static String loginUrl(String appId, String redirectUrl) throws UnsupportedEncodingException { 31 | return String.format(WxConstants.WX_LOGIN_URL, appId, URLEncoder.encode(redirectUrl, StandardCharsets.UTF_8.name())); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /commons/src/main/java/com/lyf/springboot/wx/auto/WeChatProperties.java: -------------------------------------------------------------------------------- 1 | package com.lyf.springboot.wx.auto; 2 | 3 | import org.springframework.boot.context.properties.ConfigurationProperties; 4 | 5 | /** 6 | * Created by 永锋 on 2016/11/22. 7 | */ 8 | @ConfigurationProperties( 9 | prefix = "spring.social.wx" 10 | ) 11 | public class WeChatProperties { 12 | 13 | private String appId; 14 | private String appSecret; 15 | private String appName; 16 | private int deviceType; 17 | 18 | public String getAppName() { 19 | return appName; 20 | } 21 | 22 | public void setAppName(String appName) { 23 | this.appName = appName; 24 | } 25 | 26 | public int getDeviceType() { 27 | return deviceType; 28 | } 29 | 30 | public void setDeviceType(int deviceType) { 31 | this.deviceType = deviceType; 32 | } 33 | 34 | public String getAppId() { 35 | return appId; 36 | } 37 | 38 | public void setAppId(String appId) { 39 | this.appId = appId; 40 | } 41 | 42 | public String getAppSecret() { 43 | return appSecret; 44 | } 45 | 46 | public void setAppSecret(String appSecret) { 47 | this.appSecret = appSecret; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /starter-feign/src/main/java/com/lyf/springboot/wechat/WxAuthClient.java: -------------------------------------------------------------------------------- 1 | package com.lyf.springboot.wechat; 2 | 3 | import com.lyf.springboot.wx.data.WeChatAuthResponse; 4 | import com.lyf.springboot.wx.data.WeChatUserInfo; 5 | import com.lyf.springboot.wx.utils.WxConstants; 6 | import org.springframework.cloud.openfeign.FeignClient; 7 | import org.springframework.web.bind.annotation.GetMapping; 8 | import org.springframework.web.bind.annotation.RequestParam; 9 | 10 | /** 11 | * @author 12 | */ 13 | 14 | @FeignClient(name = "wxAuthClient", url = WxConstants.API_HOST) 15 | public interface WxAuthClient { 16 | String GRANT_TYPE_AUTH = "authorization_code"; 17 | String LANG_CHINA = "zh_CN"; 18 | 19 | 20 | @GetMapping(WxConstants.AUTH_PATH) 21 | WeChatAuthResponse auth(@RequestParam("appid") String appId, 22 | @RequestParam("secret") String secret, 23 | @RequestParam("code") String authorizationCode, 24 | @RequestParam("grant_type") String grantType); 25 | 26 | @GetMapping(WxConstants.USER_INFO_PATH) 27 | WeChatUserInfo userInfo(@RequestParam("openid") String openId, 28 | @RequestParam("access_token") String accessToken, 29 | @RequestParam("lang") String lang); 30 | } 31 | -------------------------------------------------------------------------------- /demo/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | wechat-parent 7 | com.lyf.springboot.wechat 8 | 0.0.2-SNAPSHOT 9 | ../pom.xml 10 | 11 | 4.0.0 12 | 13 | demo 14 | 15 | 16 | 17 | org.springframework.boot 18 | spring-boot-starter-web 19 | 20 | 25 | 26 | 27 | com.lyf.springboot.wechat 28 | starter-feign 29 | 0.0.2-SNAPSHOT 30 | 31 | 32 | 33 | 34 | 35 | 36 | org.springframework.boot 37 | spring-boot-maven-plugin 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /commons/src/main/java/com/lyf/springboot/wx/utils/JsonHelper.java: -------------------------------------------------------------------------------- 1 | package com.lyf.springboot.wx.utils; 2 | 3 | import com.fasterxml.jackson.core.type.TypeReference; 4 | import com.fasterxml.jackson.databind.ObjectMapper; 5 | import org.apache.commons.lang3.StringUtils; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | 9 | import java.io.IOException; 10 | 11 | public class JsonHelper { 12 | 13 | private static Logger logger = LoggerFactory.getLogger(JsonHelper.class); 14 | 15 | private static ObjectMapper mapper = new ObjectMapper(); 16 | 17 | public static String toJsonString(Object o) { 18 | if (o == null) { 19 | return null; 20 | } 21 | 22 | try { 23 | String jsonString = mapper.writeValueAsString(o); 24 | return jsonString; 25 | } catch (IOException e) { 26 | logger.error("unknow error: {}", e); 27 | return null; 28 | } 29 | } 30 | 31 | public static T fromJsonString(String json, Class valueType) { 32 | if (StringUtils.isBlank(json)) { 33 | return null; 34 | } 35 | 36 | try { 37 | return mapper.readValue(json, valueType); 38 | } catch (IOException e) { 39 | logger.error("can't parse json string: {}", json, e); 40 | e.printStackTrace(); 41 | return null; 42 | } 43 | } 44 | 45 | public static T fromJsonString(String json, TypeReference type) { 46 | if (StringUtils.isBlank(json)) { 47 | return null; 48 | } 49 | try { 50 | return mapper.readValue(json, type); 51 | } catch (IOException e) { 52 | logger.error("can't parse json string: {}", json, e); 53 | return null; 54 | } 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /commons/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | wechat-parent 7 | com.lyf.springboot.wechat 8 | 0.0.2-SNAPSHOT 9 | ../pom.xml 10 | 11 | 4.0.0 12 | 13 | commons 14 | 15 | 16 | 17 | 18 | org.springframework.boot 19 | spring-boot-autoconfigure 20 | 21 | 22 | org.springframework.boot 23 | spring-boot-starter-logging 24 | 25 | 26 | org.apache.commons 27 | commons-lang3 28 | 29 | 30 | com.fasterxml.jackson.core 31 | jackson-core 32 | 33 | 34 | com.fasterxml.jackson.core 35 | jackson-databind 36 | 37 | 38 | commons-collections 39 | commons-collections 40 | 41 | 42 | org.projectlombok 43 | lombok 44 | provided 45 | 46 | 47 | -------------------------------------------------------------------------------- /commons/src/main/java/com/lyf/springboot/wx/data/WeChatUserInfo.java: -------------------------------------------------------------------------------- 1 | package com.lyf.springboot.wx.data; 2 | 3 | 4 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 5 | import com.fasterxml.jackson.databind.PropertyNamingStrategy; 6 | import com.fasterxml.jackson.databind.annotation.JsonNaming; 7 | import lombok.Data; 8 | 9 | import java.util.Arrays; 10 | 11 | /** 12 | * { 13 | * "openid":"omZu7t_MZ48ZeXLv6HV77z8_xxx", 14 | * "nickname":"蒼白", 15 | * "sex":1, 16 | * "language":"zh_CN", 17 | * "city":"西城", 18 | * "province":"北京", 19 | * "country":"中国", 20 | * "headimgurl":"http:\/\/wx.qlogo.cn\/mmopen\/ajNVdqHZLLBSVTcnqBd5s47NbibExsxoRlo0uVfppEBbjNmgUxSnTdAXtS8pcpZMicVsTO9VpwVv2aexOZxXODUQ\/0", 21 | * "privilege":[], 22 | * "unionid":"oXqrGt6JRRZJDQDEQAFUkabc" 23 | * } 24 | */ 25 | @JsonIgnoreProperties(ignoreUnknown = true) 26 | @JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class) 27 | @Data 28 | public class WeChatUserInfo { 29 | 30 | private static final int SEX_MAN = 1; 31 | 32 | private static final int SEX_WOMEN = 2; 33 | 34 | private String openid; 35 | 36 | private String nickname; 37 | 38 | private int sex; 39 | 40 | private String province; 41 | 42 | private String city; 43 | 44 | private String country; 45 | 46 | private String headimgurl; 47 | 48 | private String srcimg; 49 | 50 | private String[] privilege; 51 | 52 | private String unionid; 53 | 54 | private String language; 55 | 56 | public void beMan() { 57 | this.sex = SEX_MAN; 58 | } 59 | public void beWomen() { 60 | this.sex = SEX_WOMEN; 61 | } 62 | 63 | @Override 64 | public String toString() { 65 | return "WechatUserInfo{" + 66 | "openid='" + openid + '\'' + 67 | ", nickname='" + nickname + '\'' + 68 | ", sex=" + sex + 69 | ", province='" + province + '\'' + 70 | ", city='" + city + '\'' + 71 | ", country='" + country + '\'' + 72 | ", headimgurl='" + headimgurl + '\'' + 73 | ", privilege=" + Arrays.toString(privilege) + 74 | ", unionid='" + unionid + '\'' + 75 | '}'; 76 | } 77 | 78 | public String getLanguage() { 79 | return language; 80 | } 81 | 82 | public void setLanguage(String language) { 83 | this.language = language; 84 | } 85 | 86 | public String getSrcimg() { 87 | return srcimg; 88 | } 89 | 90 | public void setSrcimg(String srcimg) { 91 | this.srcimg = srcimg; 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /starter-httpclient/src/main/java/com/lyf/springboot/wx/service/ClientAuthService.java: -------------------------------------------------------------------------------- 1 | package com.lyf.springboot.wx.service; 2 | 3 | import com.lyf.springboot.wx.WxAuthServiceBase; 4 | import com.lyf.springboot.wx.auto.WeChatProperties; 5 | import com.lyf.springboot.wx.data.WeChatAuthResponse; 6 | import com.lyf.springboot.wx.data.WeChatUserInfo; 7 | import com.lyf.springboot.wx.utils.HttpClientUtil; 8 | import com.lyf.springboot.wx.utils.JsonHelper; 9 | import com.lyf.springboot.wx.utils.WxConstants; 10 | import lombok.extern.slf4j.Slf4j; 11 | import org.apache.commons.lang3.StringUtils; 12 | 13 | /** 14 | * @author 永锋 15 | * @date 2017/9/28 16 | */ 17 | @Slf4j 18 | public class ClientAuthService extends WxAuthServiceBase { 19 | 20 | public ClientAuthService(WeChatProperties weChatProperties) { 21 | super(weChatProperties); 22 | } 23 | 24 | @Override 25 | public WeChatAuthResponse auth(String authorizationCode) { 26 | if (StringUtils.isEmpty(authorizationCode) || "null".equalsIgnoreCase(authorizationCode)) { 27 | log.warn("error code: {}", authorizationCode); 28 | return null; 29 | } 30 | 31 | String accessUrl = getWXAccessUrl(authorizationCode, weChatProperties.getAppId(), 32 | weChatProperties.getAppSecret()); 33 | log.info("accessUrl:{}", accessUrl); 34 | String result = HttpClientUtil.getHttpsContent(accessUrl); 35 | WeChatAuthResponse response = JsonHelper.fromJsonString(result, WeChatAuthResponse.class); 36 | 37 | if (response == null || response.getErrcode() != 0 || StringUtils.isBlank(response.getOpenid())) { 38 | return null; 39 | } 40 | return response; 41 | } 42 | 43 | @Override 44 | public WeChatUserInfo userInfo(String accessToken, String openId) { 45 | String userInfoUrl = getWxUserInfoUrl(accessToken, openId); 46 | log.info("userinfo url: {}", userInfoUrl); 47 | String userInfo = HttpClientUtil.getHttpsContent(userInfoUrl); 48 | log.info("result user info: {}", userInfo); 49 | return JsonHelper.fromJsonString(userInfo, WeChatUserInfo.class); 50 | } 51 | 52 | private static String getWXAccessUrl(String code, String appId, String secret) { 53 | return WxConstants.API_HOST + WxConstants.AUTH_PATH + "?appid=" + appId + "&secret=" + secret + "&code=" + code + "&grant_type=authorization_code"; 54 | } 55 | 56 | private static String getWxUserInfoUrl(String accessToken, String openid) { 57 | return WxConstants.API_HOST + WxConstants.USER_INFO_PATH + "?access_token=" + accessToken + "&openid=" + openid + "&lang=zh_CN"; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /starter-feign/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | wechat-parent 7 | com.lyf.springboot.wechat 8 | 0.0.2-SNAPSHOT 9 | ../pom.xml 10 | 11 | starter-feign 12 | 0.0.2-SNAPSHOT 13 | starter-feign 14 | WeChat auth for Spring Boot 15 | 16 | 17 | 1.8 18 | Hoxton.SR6 19 | 20 | 21 | 22 | 23 | org.springframework.cloud 24 | spring-cloud-starter-openfeign 25 | 26 | 27 | ${project.groupId} 28 | commons 29 | ${project.version} 30 | 31 | 32 | 33 | org.projectlombok 34 | lombok 35 | true 36 | 37 | 38 | org.springframework.boot 39 | spring-boot-starter-test 40 | test 41 | 42 | 43 | org.junit.vintage 44 | junit-vintage-engine 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | org.springframework.cloud 54 | spring-cloud-dependencies 55 | ${spring-cloud.version} 56 | pom 57 | import 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | org.springframework.boot 66 | spring-boot-maven-plugin 67 | 68 | 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.lyf.springboot.wechat 7 | wechat-parent 8 | 0.0.2-SNAPSHOT 9 | 10 | commons 11 | demo 12 | starter-httpclient 13 | starter-feign 14 | 15 | pom 16 | 17 | wechat-parent 18 | Spring Boot WeChat Starter 19 | 20 | 21 | org.springframework.boot 22 | spring-boot-starter-parent 23 | 2.3.1.RELEASE 24 | 25 | 26 | 27 | 28 | UTF-8 29 | UTF-8 30 | 1.8 31 | 1.18.12 32 | Hoxton.SR6 33 | 34 | 35 | 36 | 37 | JohnsonLow(yongfeng.liu) 38 | 272462809@qq.com 39 | github.com/JohnsonLow 40 | +8 41 | 42 | 43 | 44 | 45 | 46 | org.apache.commons 47 | commons-lang3 48 | 3.10 49 | 50 | 51 | 52 | commons-collections 53 | commons-collections 54 | 3.2.2 55 | 56 | 57 | 58 | org.springframework.cloud 59 | spring-cloud-dependencies 60 | ${spring-cloud.version} 61 | pom 62 | import 63 | 64 | 65 | org.projectlombok 66 | lombok 67 | ${lombok.version} 68 | provided 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | maven-compiler-plugin 77 | 78 | ${java.version} 79 | ${java.version} 80 | ${project.build.sourceEncoding} 81 | 82 | 83 | 84 | maven-resources-plugin 85 | 86 | ${project.build.sourceEncoding} 87 | true 88 | 89 | 90 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /demo/src/main/java/com/lyf/springboot/wx/demo/WxAuthController.java: -------------------------------------------------------------------------------- 1 | package com.lyf.springboot.wx.demo; 2 | 3 | import com.lyf.springboot.wx.auto.WeChatProperties; 4 | import com.lyf.springboot.wx.data.WeChatUserInfo; 5 | import com.lyf.springboot.wx.utils.WxConstants; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.stereotype.Controller; 10 | import org.springframework.ui.Model; 11 | import org.springframework.web.bind.annotation.PathVariable; 12 | import org.springframework.web.bind.annotation.RequestMapping; 13 | 14 | import javax.servlet.http.HttpServletRequest; 15 | import javax.servlet.http.HttpSession; 16 | import java.io.UnsupportedEncodingException; 17 | 18 | /** 19 | * 此demo用于处理微信公众号授权 20 | * Created by 永锋 on 2017/9/28. 21 | */ 22 | @Controller 23 | @RequestMapping("/wx") 24 | public class WxAuthController { 25 | private static final String AUTH_REDIRECT = "redirectUrl"; 26 | private static final Logger logger = LoggerFactory.getLogger(WxAuthController.class); 27 | 28 | @Autowired 29 | private DemoService demoService; 30 | @Autowired 31 | private HttpServletRequest request; 32 | @Autowired 33 | private WeChatProperties weChatProperties; 34 | 35 | /** 36 | * @param webType 3页面点击授权 37 | * @return 38 | * @throws UnsupportedEncodingException 39 | */ 40 | @RequestMapping("/{webType}") 41 | public String redirect(@PathVariable("webType") int webType) throws UnsupportedEncodingException { 42 | if (webType == 3) { 43 | String refer = request.getHeader("Referer"); 44 | logger.info("refer:{}", refer); 45 | request.getSession().setAttribute(SessionConstants.AUTH_REDIRECT, refer); 46 | } 47 | return "redirect:" + buildRedirectUrl(); 48 | } 49 | 50 | /** 51 | * 使用即答服务号授权 52 | * 53 | * @return 54 | * @throws UnsupportedEncodingException 55 | */ 56 | private String buildRedirectUrl() throws UnsupportedEncodingException { 57 | //TODO 此处应根据实际的域名修改 58 | String redirectUrl = "http://localhost/" + request.getServerName() + "/wx/auth"; 59 | String redirect_url = WxConstants.loginUrl(weChatProperties.getAppId(), redirectUrl); 60 | logger.info("url: {}", redirect_url); 61 | return redirect_url; 62 | } 63 | 64 | @RequestMapping("/auth") 65 | public String auth(String code, String state, Model model) { 66 | String refer = request.getHeader("Referer"); 67 | try { 68 | logger.info("wx auth state : {}", state); 69 | 70 | WeChatUserInfo wxUser = demoService.authAndUser(code); 71 | if (wxUser == null) { 72 | model.addAttribute("errMsg", "微信授权失败,请返回重试"); 73 | model.addAttribute("tryUrl", "http://v.51ruhang.cn"); 74 | return "error"; 75 | } 76 | HttpSession session = request.getSession(); 77 | String redirectUrl = (String) session.getAttribute(AUTH_REDIRECT); 78 | if (redirectUrl != null) { 79 | session.removeAttribute(AUTH_REDIRECT); 80 | return "redirect:" + redirectUrl; 81 | } else { 82 | return "redirect:" + refer; 83 | } 84 | } catch (Exception exception) { 85 | return "redirect:" + refer; 86 | } 87 | } 88 | 89 | } 90 | -------------------------------------------------------------------------------- /demo/src/main/java/com/lyf/springboot/wx/demo/LoginInterceptor.java: -------------------------------------------------------------------------------- 1 | package com.lyf.springboot.wx.demo; 2 | 3 | 4 | import org.apache.commons.lang3.StringUtils; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | import org.springframework.stereotype.Component; 8 | import org.springframework.web.servlet.ModelAndView; 9 | import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; 10 | 11 | import javax.servlet.http.HttpServletRequest; 12 | import javax.servlet.http.HttpServletResponse; 13 | import javax.servlet.http.HttpSession; 14 | import java.io.IOException; 15 | import java.net.URLEncoder; 16 | import java.util.HashSet; 17 | import java.util.Set; 18 | 19 | @Component 20 | public class LoginInterceptor extends HandlerInterceptorAdapter { 21 | 22 | private static Logger logger = LoggerFactory.getLogger(LoginInterceptor.class); 23 | 24 | private static Set jumpUrls = new HashSet<>(); 25 | static { 26 | jumpUrls.add("/article/list"); 27 | jumpUrls.add("/article/list-data"); 28 | // jumpUrls.add("") 29 | } 30 | 31 | 32 | public LoginInterceptor() { 33 | } 34 | 35 | 36 | @Override 37 | public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) 38 | throws Exception { 39 | HttpSession session = request.getSession(); 40 | if(session.getAttribute(SessionConstants.SESSION_AGENT_WXUSER) != null){ 41 | response.setHeader("Cache-Control","no-store"); 42 | response.setHeader("Pragma", "no-cache"); 43 | response.setDateHeader("Expires", 0); 44 | return true; 45 | } 46 | 47 | return checkWxBrowser(request, response); 48 | } 49 | 50 | private boolean checkWxBrowser(HttpServletRequest request, HttpServletResponse response) throws IOException { 51 | StringBuffer requestUrl = request.getRequestURL(); 52 | String queryParam = request.getQueryString(); 53 | String refUrl = requestUrl.toString(); 54 | if (StringUtils.isNotBlank(queryParam)) { 55 | refUrl += "?" + queryParam; 56 | } 57 | if(jumpUrls.contains(request.getRequestURI())){ 58 | if(isWxBrowser(request)){ 59 | request.getSession().setAttribute(SessionConstants.AUTH_REDIRECT, refUrl); 60 | response.sendRedirect(String.format("/agent_wx_auth/%s?url=%s", webType(), 61 | URLEncoder.encode(refUrl, "UTF8"))); 62 | return false; 63 | } else { 64 | response.setHeader("Cache-Control","no-store"); 65 | response.setHeader("Pragma", "no-cache"); 66 | response.setDateHeader("Expires", 0); 67 | return true; 68 | } 69 | } else { 70 | request.getSession().setAttribute(SessionConstants.AUTH_REDIRECT, refUrl); 71 | response.sendRedirect(String.format("/agent_wx_auth/%s?url=%s", webType(), 72 | URLEncoder.encode(refUrl, "UTF8"))); 73 | return false; 74 | } 75 | } 76 | private static boolean isWxBrowser(HttpServletRequest request){ 77 | String userAgent = request.getHeader("user-agent"); 78 | return userAgent.contains("MicroMessenger"); 79 | } 80 | 81 | protected int webType (){ 82 | return 1; 83 | } 84 | @Override 85 | public void postHandle( 86 | HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) 87 | throws Exception { 88 | } 89 | 90 | @Override 91 | public void afterCompletion( 92 | HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) 93 | throws Exception { 94 | } 95 | 96 | } 97 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## spring-boot-wechat-starter 2 | 3 | 该项目主要功能是微信授权登陆及获取用户信息,共有两个模块 4 | - wechat-starter 主要功能模块 5 | - demo 测试模块 6 | ###wechat-starter模块 7 | #### jar依赖 8 | ```xml 9 | 10 | org.springframework.boot 11 | spring-boot-autoconfigure 12 | 13 | 14 | org.springframework.boot 15 | spring-boot-starter-logging 16 | 17 | 18 | org.apache.commons 19 | commons-lang3 20 | 21 | 22 | com.fasterxml.jackson.core 23 | jackson-core 24 | 25 | 26 | com.fasterxml.jackson.core 27 | jackson-databind 28 | 29 | 30 | org.apache.httpcomponents 31 | httpclient 32 | 33 | 34 | commons-collections 35 | commons-collections 36 | 37 | ``` 38 | #### 主要功能类 39 | 1. 配置类 40 | ```java 41 | @ConfigurationProperties(prefix = "spring.social.wx") 42 | public class WxProperties extends SocialProperties { 43 | 44 | private String appName; 45 | 46 | private int deviceType; 47 | 48 | public String getAppName() { 49 | return appName; 50 | } 51 | 52 | public void setAppName(String appName) { 53 | this.appName = appName; 54 | } 55 | 56 | public int getDeviceType() { 57 | return deviceType; 58 | } 59 | 60 | public void setDeviceType(int deviceType) { 61 | this.deviceType = deviceType; 62 | } 63 | } 64 | ``` 65 | 2.逻辑类 66 | ```java 67 | package com.lyf.springboot.wx; 68 | 69 | import com.lyf.springboot.wx.auto.WeChatProperties; 70 | import com.lyf.springboot.wx.data.WeChatAuthResponse; 71 | import com.lyf.springboot.wx.data.WeChatUserInfo; 72 | import com.lyf.springboot.wx.utils.HttpClientUtil; 73 | import com.lyf.springboot.wx.utils.JsonHelper; 74 | import com.lyf.springboot.wx.utils.WxConstants; 75 | import org.apache.commons.lang3.StringUtils; 76 | import org.slf4j.Logger; 77 | import org.slf4j.LoggerFactory; 78 | import org.springframework.beans.factory.annotation.Autowired; 79 | 80 | /** 81 | * Created by 永锋 on 2017/9/28. 82 | */ 83 | public class WxAuthService { 84 | private static Logger logger = LoggerFactory.getLogger(WxAuthService.class); 85 | @Autowired 86 | private WxProperties wxProperties; 87 | 88 | public WxAuthService() { 89 | } 90 | 91 | /** 92 | * 进行微信授权,获取用户信息 存入进本库 和 关系库 93 | * @param code 94 | * @return 95 | */ 96 | public WeChatUserInfo auth(String code){ 97 | if(StringUtils.isEmpty(code) || "null".equalsIg(code)){ 98 | logger.warn("error code: {}", code); 99 | return null; 100 | } 101 | 102 | String accessUrl = WxHelper.getWXAccessUrl(code, wxProperties.getAppId(), 103 | wxProperties.getAppSecret()); 104 | logger.info("accessUrl:{}", accessUrl); 105 | String result = HttpClientUtil.getHttpsContent(accessUrl); 106 | WeChatAuthResponse response = JsonHelper.fromJsonString(result, WeChatAuthResponse.class); 107 | 108 | if(response == null || response.getErrcode() != 0 || StringUtils.isBlank(response.getOpenid())){ 109 | return null; 110 | } 111 | WeChatUserInfo userInfo = WxHelper.requestUserInfo(response.getAccess_token(), response.getOpenid()); 112 | if(userInfo == null || StringUtils.isBlank(userInfo.getUnionid())){ 113 | return null; 114 | } 115 | return userInfo; 116 | } 117 | } 118 | ``` 119 | 3. 自动注入类 120 | ```java 121 | package com.lyf.springboot.wx.auto; 122 | 123 | import com.lyf.springboot.wx.WxAuthService; 124 | import org.springframework.boot.context.properties.EnableConfigurationProperties; 125 | import org.springframework.context.annotation.Bean; 126 | import org.springframework.context.annotation.Configuration; 127 | 128 | /** 129 | * Created by 永锋 on 2017/9/28. 130 | */ 131 | @Configuration 132 | @EnableConfigurationProperties(WxProperties.class) 133 | public class WxAutoConfiguration { 134 | @Bean(name = "wxAuthService") 135 | public WxAuthService wxAuthService(){ 136 | return new WxAuthService(); 137 | } 138 | } 139 | ``` 140 | 4.配置自动注入类,在resources/META-INF/目录下创建spring.factories文件 141 | 142 | ``` 143 | org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ 144 | com.lyf.springboot.wx.auto.WxAutoConfiguration 145 | ``` 146 | 147 | 5. 对WxProperties类的属性进行配置,在resources/META-INF/目录下创建spring-configuration-metadata.json文件 148 | ```javascript 149 | { 150 | "properties": [ 151 | { 152 | "name": "spring.social.wx.app-id", 153 | "type": "java.lang.String", 154 | "sourceType": "com.lyf.springboot.wx.auto.WeChatProperties" 155 | }, 156 | { 157 | "name": "spring.social.wx.app-secret", 158 | "type": "java.lang.String", 159 | "sourceType": "com.lyf.springboot.wx.auto.WeChatProperties" 160 | }, 161 | { 162 | "name": "spring.social.wx.app-name", 163 | "type": "java.lang.String", 164 | "sourceType": "com.lyf.springboot.wx.auto.WeChatProperties" 165 | }, 166 | { 167 | "name": "spring.social.wx.device-type", 168 | "type": "java.lang.String", 169 | "description": "wx platform type, 1 for app, 2 for web", 170 | "defaultValue": 1, 171 | "sourceType": "com.lyf.springboot.wx.auto.WeChatProperties" 172 | } 173 | ], 174 | "hints": [] 175 | } 176 | ``` 177 | ### starter的使用 178 | > 0.0.2中更新了spring boot为2.3.1,如果需要在自己项目中使用,建议根据自己的springboot版本进行调整 179 | 1. maven中引入starter-feign 180 | ```xml 181 | 182 | 183 | com.lyf.springboot.wechat 184 | starter-feign 185 | 0.0.2-SNAPSHOT 186 | 187 | 188 | 189 | 196 | ``` 197 | 2. 在application.yml中配置自己的微信App信息 198 | ```yml 199 | spring: 200 | social: 201 | wx: 202 | app-id: # 配置自己申请的appId 203 | app-secret: #配置为自己申请的appSecret 204 | app-name: #此处是为了区分多个app进行的配置 可选 205 | device-type: 2 206 | ``` 207 | 3. 在自己的controller里面调用wechat-starter的逻辑类,实现授权登陆(获取用户信息) 208 | -------------------------------------------------------------------------------- /starter-httpclient/src/main/java/com/lyf/springboot/wx/utils/HttpClientUtil.java: -------------------------------------------------------------------------------- 1 | package com.lyf.springboot.wx.utils; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.apache.http.HttpStatus; 5 | import org.apache.http.client.HttpRequestRetryHandler; 6 | import org.apache.http.client.entity.UrlEncodedFormEntity; 7 | import org.apache.http.client.methods.CloseableHttpResponse; 8 | import org.apache.http.client.methods.HttpGet; 9 | import org.apache.http.client.methods.HttpPost; 10 | import org.apache.http.client.protocol.HttpClientContext; 11 | import org.apache.http.config.ConnectionConfig; 12 | import org.apache.http.config.Registry; 13 | import org.apache.http.config.RegistryBuilder; 14 | import org.apache.http.config.SocketConfig; 15 | import org.apache.http.conn.socket.ConnectionSocketFactory; 16 | import org.apache.http.conn.socket.PlainConnectionSocketFactory; 17 | import org.apache.http.conn.ssl.SSLConnectionSocketFactory; 18 | import org.apache.http.conn.ssl.SSLContextBuilder; 19 | import org.apache.http.conn.ssl.TrustStrategy; 20 | import org.apache.http.impl.client.CloseableHttpClient; 21 | import org.apache.http.impl.client.HttpClients; 22 | import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; 23 | import org.apache.http.message.BasicNameValuePair; 24 | import org.apache.http.protocol.HttpContext; 25 | import org.apache.http.util.EntityUtils; 26 | 27 | import javax.net.ssl.SSLContext; 28 | import java.io.IOException; 29 | import java.net.URI; 30 | import java.nio.charset.StandardCharsets; 31 | import java.security.KeyManagementException; 32 | import java.security.KeyStoreException; 33 | import java.security.NoSuchAlgorithmException; 34 | import java.util.ArrayList; 35 | import java.util.List; 36 | import java.util.Map; 37 | import java.util.Set; 38 | 39 | @Slf4j 40 | public class HttpClientUtil { 41 | 42 | public static final int DEFAULE_RETRY = 3; 43 | 44 | public static final int KMAXTOTAL = 2000; 45 | 46 | public static final int MAXPERROUTE = 2000; 47 | 48 | public static CloseableHttpClient buildHttpClient() { 49 | return buildHttpClient(DEFAULE_RETRY, KMAXTOTAL, MAXPERROUTE); 50 | } 51 | 52 | /** 53 | * create http client 54 | * 55 | * @param retryNum 重试次数 56 | * @param maxTotal 最大连接数 57 | * @param maxPerRoute 每个路由基础的连接数 58 | * @return 59 | */ 60 | public static CloseableHttpClient buildHttpClient(Integer retryNum, Integer maxTotal, 61 | Integer maxPerRoute) { 62 | final int kRetryNum = retryNum == null ? DEFAULE_RETRY : retryNum; 63 | 64 | HttpRequestRetryHandler retryHandler = (exception, executionCount, context) -> { 65 | if (executionCount > kRetryNum) { 66 | // Do not retry if over max retry count 67 | return false; 68 | } 69 | return true; 70 | }; 71 | 72 | final int kMaxTotal = maxTotal == null ? KMAXTOTAL : maxTotal; 73 | final int kMaxPerRoute = maxPerRoute == null ? MAXPERROUTE : maxPerRoute; 74 | 75 | Registry r = 76 | RegistryBuilder.create() 77 | .register("http", PlainConnectionSocketFactory.INSTANCE).build(); 78 | 79 | PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(r); 80 | 81 | // ConnectionConfig 82 | ConnectionConfig connectionConfig = ConnectionConfig.custom().setCharset(StandardCharsets.UTF_8).build(); 83 | SocketConfig socketConfig = 84 | SocketConfig.custom().setTcpNoDelay(true).setSoKeepAlive(true).setSoReuseAddress(true) 85 | .build(); 86 | cm.setDefaultConnectionConfig(connectionConfig); 87 | cm.setDefaultSocketConfig(socketConfig); 88 | // 将最大连接数增加到2000 89 | cm.setMaxTotal(kMaxTotal); 90 | // 将每个路由基础的连接增加到2000 91 | cm.setDefaultMaxPerRoute(kMaxPerRoute); 92 | 93 | return HttpClients.custom().setConnectionManager(cm).setRetryHandler(retryHandler).build(); 94 | } 95 | 96 | public static String getContent(String requestUrl, Map header) { 97 | CloseableHttpResponse response = null; 98 | CloseableHttpClient httpClient = HttpClients.createDefault(); 99 | String returnValue = null; 100 | log.debug("request url: {}", requestUrl); 101 | try { 102 | 103 | HttpGet httpGet = new HttpGet(requestUrl); 104 | 105 | if (header != null && header.size() > 0) { 106 | for (Map.Entry entry : header.entrySet()) { 107 | httpGet.addHeader(entry.getKey(), entry.getValue()); 108 | } 109 | } 110 | 111 | HttpContext context = HttpClientContext.create(); 112 | 113 | response = httpClient.execute(httpGet, context); 114 | int statusCode = response.getStatusLine().getStatusCode(); 115 | 116 | returnValue = new String(EntityUtils.toByteArray(response.getEntity()), StandardCharsets.UTF_8); 117 | log.debug(returnValue); 118 | 119 | log.debug("status code {}", statusCode); 120 | if (statusCode != HttpStatus.SC_OK) { 121 | throw new RuntimeException("目前提交不了,联系码农一下吧 :)"); 122 | } 123 | 124 | } catch (Exception e) { 125 | log.error("Http Get Content", e); 126 | } finally { 127 | try { 128 | closeHttpResource(response, httpClient); 129 | } catch (IOException e) { 130 | log.warn("Close Http Resource error ", e); 131 | } 132 | } 133 | return returnValue; 134 | 135 | 136 | } 137 | 138 | 139 | public static String getContent(String requestUrl) { 140 | return getContent(requestUrl, null); 141 | } 142 | 143 | public static String getHttpsContent(String url) { 144 | CloseableHttpResponse response = null; 145 | String returnValue = null; 146 | CloseableHttpClient httpClient = HttpClientUtil.createSSLClientDefault(); 147 | try { 148 | HttpGet get = new HttpGet(); 149 | get.setURI(new URI(url)); 150 | response = httpClient.execute(get); 151 | returnValue = new String(EntityUtils.toByteArray(response.getEntity()), StandardCharsets.UTF_8); 152 | log.debug(returnValue); 153 | } catch (Exception e) { 154 | log.error("Http Get Content", e); 155 | } finally { 156 | try { 157 | closeHttpResource(response, httpClient); 158 | } catch (IOException e) { 159 | log.warn("Close Http Resource error ", e); 160 | } 161 | } 162 | return returnValue; 163 | } 164 | 165 | 166 | public static String postHttpContent(String url, Map param, Map header) { 167 | CloseableHttpResponse response = null; 168 | String returnValue = null; 169 | CloseableHttpClient httpClient = HttpClientUtil.createSSLClientDefault(); 170 | try { 171 | HttpPost post = new HttpPost(); 172 | if (header != null && header.size() > 0) { 173 | for (Map.Entry entry : header.entrySet()) { 174 | post.addHeader(entry.getKey(), entry.getValue()); 175 | } 176 | } 177 | 178 | List nvps = new ArrayList<>(); 179 | 180 | Set keySet = param.keySet(); 181 | for (String key : keySet) { 182 | nvps.add(new BasicNameValuePair(key, param.get(key))); 183 | } 184 | 185 | post.setEntity(new UrlEncodedFormEntity(nvps, StandardCharsets.UTF_8)); 186 | 187 | post.setURI(new URI(url)); 188 | response = httpClient.execute(post); 189 | returnValue = new String(EntityUtils.toByteArray(response.getEntity()), StandardCharsets.UTF_8); 190 | log.debug(returnValue); 191 | } catch (Exception e) { 192 | log.error("Http Post Content", e); 193 | } finally { 194 | try { 195 | closeHttpResource(response, httpClient); 196 | } catch (IOException e) { 197 | log.warn("Close Http Resource error ", e); 198 | } 199 | } 200 | return returnValue; 201 | } 202 | 203 | 204 | public static void closeHttpResource(CloseableHttpResponse response, CloseableHttpClient httpClient) 205 | throws IOException { 206 | if (response != null) { 207 | response.close(); 208 | } 209 | 210 | if (httpClient != null) { 211 | httpClient.close(); 212 | } 213 | } 214 | 215 | public static CloseableHttpClient createSSLClientDefault() { 216 | try { 217 | SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() { 218 | @Override 219 | public boolean isTrusted(java.security.cert.X509Certificate[] x509Certificates, String s) 220 | throws java.security.cert.CertificateException { 221 | return false; 222 | } 223 | }).build(); 224 | SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext); 225 | return HttpClients.custom().setSSLSocketFactory(sslsf).build(); 226 | } catch (KeyManagementException e) { 227 | e.printStackTrace(); 228 | } catch (NoSuchAlgorithmException e) { 229 | e.printStackTrace(); 230 | } catch (KeyStoreException e) { 231 | e.printStackTrace(); 232 | } 233 | throw new RuntimeException("create ssl error"); 234 | } 235 | 236 | } 237 | --------------------------------------------------------------------------------