Springboot网站第三方登录——QQ登录

Springboot网站第三方登录——QQ登录

这段时间为了做这几个第三方登录,走了很多弯路,跳了很多坑,为以后使用特地记录下来。

由于做了多个登录,所以代码做了一定程度的封装,大致如下:

//多个登录差不多都要共用这些方法,所以统一放到这个接口中
public interface AuthService {
    public abstract String getAccessToken(String code);
    public abstract String getOpenId(String accessToken);
    public abstract String refreshToken(String code);
    public abstract String getAuthorizationUrl() throws UnsupportedEncodingException;
    public abstract JSONObject getUserInfo(String accessToken,String openId);
}

由于全部是自己封装的,所以http请求的代码也是所有的登录共用的,这里统一放放到了类DefaultAuthServiceImpl中,代码如下:

public abstract class DefaultAuthServiceImpl implements AuthService{
    public static RestTemplate getRestTemplate() {// 手动添加
        SimpleClientHttpRequestFactory requestFactory=new SimpleClientHttpRequestFactory();
        requestFactory.setReadTimeout(120000);
        List<HttpMessageConverter<?>> messageConverters = new LinkedList<>();
        messageConverters.add(new ByteArrayHttpMessageConverter());
        messageConverters.add(new StringHttpMessageConverter(StandardCharsets.UTF_8));
        messageConverters.add(new ResourceHttpMessageConverter());
        messageConverters.add(new SourceHttpMessageConverter<Source>());
        messageConverters.add(new AllEncompassingFormHttpMessageConverter());
        messageConverters.add(new MappingJackson2HttpMessageConverter());
        RestTemplate restTemplate=new RestTemplate(messageConverters);
        restTemplate.setRequestFactory(requestFactory);
        return restTemplate;
    }
}

由此,所有的登录Service只需要继承AuthService即可。


QQ登录

1.首先去QQ互联注册一个账号,网站地址:https://connect.qq.com/,添加一个应用,具体怎么申请以及需要填写的信息,腾讯官网有详细文档。注册并完成相应信息填写后,可以在应用管理中查到应用的APP ID和APP Key。
2.QQ登录的后台代码如下:

步骤一:

创建一个继承AuthService的接口,QQAuthService,如下:
public interface QQAuthService extends AuthService {
}

步骤二:

QQ登录的详细代码实现如下:
@Service
public class QQAuthServiceImpl extends DefaultAuthServiceImpl implements QQAuthService {

    private Logger logger = LoggerFactory.getLogger(QQAuthServiceImpl.class);
    //QQ 登陆页面的URL
    private final static String AUTHORIZATION_URL =
            "https://graph.qq.com/oauth2.0/authorize?response_type=code&client_id=%s&redirect_uri=%s&scope=%s";
    //获取token的URL
    private final static String ACCESS_TOKEN_URL = "https://graph.qq.com/oauth2.0/token?grant_type=authorization_code&client_id=%s&client_secret=%s&code=%s&redirect_uri=%s";

    // 获取用户 openid 的 URL
    private static final String OPEN_ID_URL = "https://graph.qq.com/oauth2.0/me?access_token=%s";

    // 获取用户信息的 URL,oauth_consumer_key 为 apiKey
    private static final String USER_INFO_URL = "https://graph.qq.com/user/get_user_info?access_token=%s&oauth_consumer_key=%s&openid=%s";

    // 下面的属性可以通过配置读取
    private  static final String CALLBACK_URL = "http://XXX/XX/XX"; // QQ 在登陆成功后回调的 URL,这个 URL 必须在 QQ 互联里填写过
    private  static final String API_KEY  = "xxxxxx";                                      // QQ 互联应用管理中心的 APP ID
    private  static final String API_SECRET = "xxxxxx";               // QQ 互联应用管理中心的 APP Key
    private  static final String SCOPE       = "get_user_info";                                  // QQ 互联的 API 接口,访问用户资料

    /**
     * @return QQ 登陆页面的 URL
     */
    @Override
    public String getAuthorizationUrl() {
        String url = String.format(AUTHORIZATION_URL,API_KEY,CALLBACK_URL,SCOPE);
        return url;
    }

    @Override
    public String getAccessToken(String code) {
        String url = String.format(ACCESS_TOKEN_URL,API_KEY,API_SECRET,code, CALLBACK_URL);
        UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(url);
        URI uri = builder.build().encode().toUri();

        String resp = getRestTemplate().getForObject(uri, String.class);
        logger.error("getAccessToken resp = "+resp);
        if(resp.contains("access_token")){
            Map<String,String> map = getParam(resp);
            String access_token = map.get("access_token");
            return access_token;
        }else{
            throw new ServiceException(resp);
        }
    }
    //由于QQ的几个接口返回类型不一样,此处是获取key-value类型的参数
    private Map<String,String> getParam(String string){
        Map<String,String> map = new HashMap();
        String[] kvArray = string.split("&");
        for(int i = 0;i<kvArray.length;i++){
            String[] kv = kvArray[i].split("=");
            map.put(kv[0],kv[1]);
        }
        return map;
    }
    //QQ接口返回类型是text/plain,此处将其转为json
    public JSONObject ConvertToJson(String string){
        string = string.substring(string.indexOf("(")+1,string.length());
        string = string.substring(0,string.indexOf(")"));
        logger.error("ConvertToJson s = "+string);
        JSONObject jsonObject = JSONObject.parseObject(string);
        return jsonObject;
    }

    @Override
    public String getOpenId(String accessToken) {
        String url = String.format(OPEN_ID_URL,accessToken);
        UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(url);
        URI uri = builder.build().encode().toUri();

        String resp = getRestTemplate().getForObject(uri, String.class);
        logger.error("getAccessToken resp = "+resp);
        if(resp.contains("openid")){
            JSONObject jsonObject = ConvertToJson(resp);
            String openid = jsonObject.getString("openid");
            return openid;
        }else{
            throw new ServiceException(resp);
        }
    }

    /**
     * https://graph.qq.com/user/get_user_info?access_token=YOUR_ACCESS_TOKEN&oauth_consumer_key=YOUR_APP_ID&openid=YOUR_OPENID
     * @param accessToken
     * @param openId
     * @return
     */
    @Override
    public JSONObject getUserInfo(String accessToken, String openId){

        openId = getOpenId(accessToken);
        String url = String.format(USER_INFO_URL,accessToken, API_KEY, openId);
        UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(url);
        URI uri = builder.build().encode().toUri();

        String resp = getRestTemplate().getForObject(uri,String.class);
        JSONObject data = JSONObject.parseObject(resp);
        logger.error("resp = "+data);
        JSONObject result = new JSONObject();
        result.put("id",openId);
        result.put("nickName",data.getString("nickname"));
        result.put("avatar",data.getString("figureurl_qq_1"));

        return result;
    }

    @Override
    public String refreshToken(String code) {
        return null;
    }
}

步骤三:
在Controller中调用,代码如下:

@RestController
@RequestMapping(value = "/auth")
public class AuthController {
     private Logger logger = LoggerFactory.getLogger(AuthController.class);
      @Autowired
    private QQAuthService qqAuthService;
    @Autowired
    private UserService userService;

 //访问登陆页面,然后会跳转到 QQ 的登陆页面
    @RequestMapping(value = "/qqLoginPage",method = RequestMethod.GET)
    public JSONObject qqLogin() throws Exception {
        String uri = qqAuthService.getAuthorizationUrl();
        return loginPage(uri);
    }
}
//qq授权后会回调此方法,并将code传过来
@RequestMapping("/qq")
    public void getQQCode(String code, HttpServletRequest request,HttpServletResponse response) throws Exception {
        //根据code获取token
        String accessToken = qqAuthService.getAccessToken(code);
        // 保存 accessToken 到 cookie,过期时间为 30 天,便于以后使用
        Cookie cookie = new Cookie("accessToken", accessToken);
        cookie.setMaxAge(60 * 24 * 30);
        response.addCookie(cookie);

        //本网站是将用户的唯一标识存在用户表中,大家也可以加一张表,存储用户和QQ的对应信息。
        //根据openId判断用户是否已经绑定过
        String openId = qqAuthService.getOpenId(accessToken);
        KmsUser user = userService.getUserByCondition(openId);

        if (user == null) {
        //如果用户不存在,则跳转到绑定页面
            response.sendRedirect(request.getContextPath() + "/student/html/index.min.html#/bind?type="+Constants.LOGIN_TYPE_QQ);
        } else {
            //如果用户已存在,则直接登录
            response.sendRedirect(request.getContextPath() + "/student/html/index.min.html#/app/home?open_id=" + openId);
        }
    }

步骤四:
前台js中,先请求auth/qqLoginPage,获取授权地址,等用户授权后会回调/auth/qq,在此方法中进行逻辑处理即可。

展开阅读全文

没有更多推荐了,返回首页