第三方登录接入-qq,weibo-java

开发之前

需求:网站接入qq,sina微博登录,本文最后付效果图:

说明:大部分网站本身是需要用户概念的,很多操作依附于用户,而qq或微博作为一种登录方式指向用户而已,我参考了一下其他网站的做法,

一般有如下两种做法:

1,强制绑定:用户第一次通过qq登录时必须与该网站账户绑定,也就是用户必须要先有一个此网站账户才能登录成功

2,互相独立,用户第一次通过qq登录时直接重新为用户注册一个账户,如以用户名为qq_123456直接注册一个账户,与其他账户无关;

 

站在用户角度考虑下,可能需要更多的选择性,因此我是如下考虑的:

用户登录后在个人中心中也可设置绑定。

---------------------------------------------------------------------------------------------------

文档说明

现在大部分第三方的登录OAuth2.0为标准,所以开发流程基本都一致,一般都是一下步骤:

1,申请接入,获取appid&appkey(接入后又第三方发放)

2,用户登录第三方下发token,

3,通过token获取用户唯一标示,一般是一个openId

api地址:

qq:http://wiki.connect.qq.com/api列表

sina:http://open.weibo.com/wiki/授权机制

qq&sina也提供了java sdk

https://github.com/sunxiaowei2014/weibo4j-oauth2-beta3.1.1/

http://qzonestyle.gtimg.cn/qzone/vas/opensns/res/doc/qqConnect_Server_SDK_java_v2.0.zip

sina的虽然开源了,但里面很多代码写的有问题,用的之后需要注意

------------------------------------------------------------------------------------------------------------

开发

数据结构方面需要加以一张表用来维护登录方式和用户关联(通过openId以及登录方式确定唯一性)

网站引入,appid,appkey,回调地址的配置有不再赘述了

代码基本上参照sdk中的demo就可以了,

简单贴一下controller(springMVC架构)中的代码吧

AUthController为父类,存放一些第三方登录的通用方法,BindController为用户绑定提供方法,第三方登录的controller基本上就是一个登录跳转方法,一个回调方法,以及一些接口api的调用

public abstract class AuthController extends BaseController {
    @Resource
    private JavaMailSender mailSender;
    
    @Resource
    private IBAuthService authService;

    protected LoginUser getU(BAuth auth){
        LoginUser loginUser= new LoginUser();
        loginUser.setAccessToken(auth.getAccessToken());
        loginUser
                .setIcon(auth.getIcon() == null ? IPortalConstants.defaultIconUrl
                        : auth.getIcon());
        loginUser.setId(auth.getUser().getId());
        loginUser.setLoginType(auth.getType());
        loginUser.setNickName(auth.getNickName() == null ? auth.getUser()
                .getNickName() : auth.getNickName());
        loginUser.setOpenId(auth.getOpenId());
        return loginUser;
    }
    
    protected LoginUser getU(User user){
        LoginUser loginUser= new LoginUser();
        loginUser
                .setIcon(user.getIcon() == null ? IPortalConstants.defaultIconUrl
                        : user.getIcon());
        loginUser.setId(user.getId());
        loginUser.setLoginType(AuthType.bresume.getCode());
        loginUser.setNickName( user.getNickName());
        return loginUser;
    }
    
    protected boolean setUser2Session(BAuth auth){
        LoginUser loginuser = this.getU(auth);
        SessionContextHolder.getSession().setAttribute(IPortalConstants.SESSION_KEY_LOGIN_USER, loginuser);
        return true;
    }
    
    protected boolean setUser2Session(User user){
        LoginUser loginuser = this.getU(user);
        SessionContextHolder.getSession().setAttribute(IPortalConstants.SESSION_KEY_LOGIN_USER, loginuser);
        return true;
    }
    
    protected void sendRegisterMail(User user,String code) {
        PropertiesLoader loader = new PropertiesLoader("mail.properties");

        Map<String, Object> map = new HashMap<String, Object>();
        Email email = new Email();
        email.setSender(loader.getProperty("mail.from"));
        email.setAddress(user.getEmail());
        
        email.setSubject(loader.getProperty("mail.register.success.subject"));
        // 从模板生成
        HashMap<String, Object> param = new HashMap<String, Object>();
        param.put("userName", user.getUserName());
        param.put("userId", user.getId());
        param.put("code", code);
        email.setContent(MailUtils.getMailText(param,
                loader.getProperty("mail.register.success.content")));
        map.put("email", email);
        MailUtils.sendMailByAsynchronousMode(map, mailSender);

    }
    
    protected String callBack(Model model,BAuth newAuth){
        BAuth oldAuth = authService.findOne(newAuth.getOpenId(),newAuth.getType());
        if (oldAuth != null && oldAuth.getUser() != null) {
            // 判定有登录记录
            //刷新accessToken
            oldAuth.setAccessToken(newAuth.getAccessToken());
            oldAuth.setExpiresIn(newAuth.getExpiresIn());
            oldAuth.setIcon(newAuth.getIcon());
            oldAuth.setNickName(newAuth.getNickName());
            oldAuth.setRefreshAccessTime(new Date());
            authService.update(oldAuth);
            this.setUser2Session(oldAuth);
            return "redirect:/index";
        } else if(oldAuth==null) {
            // 判定首次登录,记录
            oldAuth = new BAuth();
            oldAuth.setAccessToken(newAuth.getAccessToken());
            oldAuth.setExpiresIn(newAuth.getExpiresIn());
            oldAuth.setCreatedTime(new Date());
            oldAuth.setIcon(newAuth.getIcon());
            oldAuth.setNickName(newAuth.getNickName());
            oldAuth.setOpenId(newAuth.getOpenId());
            oldAuth.setRefreshAccessTime(new Date());
            oldAuth.setType(newAuth.getType());
            authService.save(oldAuth);
            //用户绑定,跳转页面
            model.addAttribute("openId", newAuth.getOpenId());
            model.addAttribute("loginFrom", newAuth.getType());
            return "site/bindAuth.jsp";
        }else{
            // 登录过但因某种原因为绑定账户
            oldAuth.setAccessToken(newAuth.getAccessToken());
            oldAuth.setExpiresIn(newAuth.getExpiresIn());
            oldAuth.setIcon(newAuth.getIcon());
            oldAuth.setNickName(newAuth.getNickName());
            oldAuth.setRefreshAccessTime(new Date());
            authService.update(oldAuth);
            //用户绑定,跳转页面
            model.addAttribute("openId", newAuth.getOpenId());
            model.addAttribute("loginFrom", newAuth.getType());
            return "site/bindAuth.jsp";
        }

    }
    
}
AuthController
@RequestMapping("/")
@Controller
public class BindController extends AuthController {

    @Resource
    private IUserService userService;

    @Resource
    private IBAuthService authService;

    @Resource
    private IUserVerifiedService verifiedService;

    @Resource
    private JavaMailSender mailSender;

    @RequestMapping("/ingore-bind")
    public String ingore_bind(
            @RequestParam(value = "loginFrom", required = true) Integer loginFrom,
            @RequestParam(value = "openId", required = true) String openId,
            ModelMap model, HttpServletResponse response) {
        BAuth auth = authService.findOne(openId, loginFrom);
        if (auth == null) {
            return "404";
        }
        if (auth.getUser() == null) {
            User user = new User();
            /*
             * user.setUserName(userName); user.setPassword(password);
             */
            // user.setEmail(email);
            user.setNickName(auth.getNickName());
            user.setIcon(auth.getIcon());

            user.setRegisterType(AuthType.fromCode(loginFrom).getRt().getType());
            user.setType(UserType.PERSIONAL.getCode());
            user.setLevel(0);
            userService.registerFromAuth(user);
            auth.setUser(user);
            authService.save(auth);
        }

        this.setUser2Session(auth);
        return "redirect:/index";
    }

    @RequestMapping("/login-bind")
    public @ResponseBody JSONObject bind(
            @RequestParam(value = "loginFrom", required = true) Integer loginFrom,
            @RequestParam(value = "openId", required = true) String openId,
            @RequestParam(value = "email", required = true) String email,
            @RequestParam(value = "password", required = true) String password,
            ModelMap model, HttpServletResponse response) {

        BAuth auth = authService.findOne(openId, loginFrom);
        if (auth == null) {
            return this.toJSONResult(false,"404");
        }

        if (auth.getUser() == null) {
            try {
                // 登陆校验
                User user = userService.loginCheck(email, password);
                auth.setUser(user);
                authService.update(auth);
            } catch (CoreException e) {
                if (e.getErrorCode() == PortalErrorCode.USER_PASSWORD_ERROR_TIMES_EXCEED_ERROR) {
                    return this.toJSONResult(false,
                            this.getMessage(e, e.getArgs()));
                } else {

                    return this.toJSONResult(false, this.getMessage(e));
                }
            }
        }

        this.setUser2Session(auth);
        return this.toJSONResult(true);
    }

    @RequestMapping("/regist-bind")
    public @ResponseBody JSONObject registBind(
            @RequestParam(value = "loginFrom", required = true) Integer loginFrom,
            @RequestParam(value = "openId", required = true) String openId,
            @RequestParam(value = "email", required = true) String email,
            @RequestParam(value = "password", required = true) String password,
            ModelMap model, HttpServletResponse response) {
        BAuth auth = authService.findOne(openId, loginFrom);
        if (auth == null) {
            return this.toJSONResult(false);
        }
        if (auth.getUser() == null) {
            User user=new User();
//            user.setUserName(userName);
            user.setPassword(password);
            user.setEmail(email);

            try {
                user.setRegisterType(RegisterType.PORTAL_REGISTER.getType());
                user.setType(UserType.PERSIONAL.getCode());
                user.setLevel(0);
                user.setNickName(auth.getNickName());
                user.setIcon(auth.getIcon());
                userService.register(user);
                //生成邮箱验证码
                UserVerified uv = new UserVerified(user);
                verifiedService.save(uv);
                // 发送注册成功的邮件
                if (CommonUtils.isNotEmpty(user.getEmail())) {
                    sendRegisterMail(user,uv.getCode());
                }
            } catch (CoreException e) {
                return this.toJSONResult(false, this.getMessage(e));
            }
            auth.setUser(user);
            authService.save(auth);
        }

        this.setUser2Session(auth);
        return this.toJSONResult(true);
    }

}
BindController
@RequestMapping("/")
@Controller
public class QQController extends AuthController {

    @Resource
    private IBAuthService authService;

    @Resource
    private IUserService userService;

    @RequestMapping("/qqlogin")
    public void index(HttpServletRequest request, HttpServletResponse response,
            Model model) throws IOException {
        response.setContentType("text/html;charset=utf-8");
        try {
            response.sendRedirect(new Oauth().getAuthorizeURL(request));
            LOGGER.info("login by qq");
        } catch (QQConnectException e) {
            e.printStackTrace();
        }
    }

    @RequestMapping("/qq_callback")
    public String callback(HttpServletRequest request,
            HttpServletResponse response, Model model) {
        try {
            AccessToken accessTokenObj = (new Oauth())
                    .getAccessTokenByRequest(request);

            String accessToken = null, openID = null;
            long tokenExpireIn = 0L;

            if (accessTokenObj.getAccessToken().equals("")) {
                LOGGER.error("QQ Login failed,caused by 没有获取到响应参数");
                return "404";
            }

            accessToken = accessTokenObj.getAccessToken();
            tokenExpireIn = accessTokenObj.getExpireIn();
            LOGGER.info("Get accessToken from qq,accessToken:" + accessToken
                    + ",tokenExpireIn" + tokenExpireIn);

            // 利用获取到的accessToken 去获取当前用的openid
            OpenID openIDObj = new OpenID(accessToken);
            openID = openIDObj.getUserOpenID();
            LOGGER.info("利用获取到的accessToken:" + accessToken
                    + ", 去获取到当前用户openid:" + openID + ".");

            String icon = null, nickName = null;
            // 去获取用户在Qzone的昵称等信息
            UserInfo qzoneUserInfo = new UserInfo(accessToken, openID);
            UserInfoBean userInfoBean = qzoneUserInfo.getUserInfo();

            if (userInfoBean.getRet() == 0) {
                nickName = userInfoBean.getNickname();
                // userInfoBean.getGender();

                icon = userInfoBean.getAvatar().getAvatarURL30();
                // userInfoBean.getAvatar().getAvatarURL50();
                // userInfoBean.getAvatar().getAvatarURL100();
            } else {
                LOGGER.error("很抱歉,我们没能正确获取到您的信息,原因是:" + userInfoBean.getMsg());
            }

            BAuth newAuth = new BAuth();
            newAuth.setAccessToken(accessToken);
            newAuth.setExpiresIn(tokenExpireIn);
            newAuth.setIcon(icon);
            newAuth.setNickName(nickName);
            newAuth.setOpenId(openID);
            newAuth.setType(AuthType.QQ.getCode());
            return this.callBack(model, newAuth);
            
            // 通过openid判断首次登录与否
        /*    BAuth bauth = authService.findOne(openID, AuthType.QQ.getCode());
            if (bauth != null && bauth.getUser() != null) {
                // 判定有登录记录
                //刷新accessToken
                bauth.setAccessToken(accessToken);
                bauth.setExpiresIn(tokenExpireIn);
                bauth.setIcon(icon);
                bauth.setNickName(nickName);
                bauth.setRefreshAccessTime(new Date());
                authService.update(bauth);
                this.setUser2Session(bauth);
                return "redirect:/index";
            } else if(bauth==null) {
                // 判定首次登录,记录
                bauth = new BAuth();
                bauth.setAccessToken(accessToken);
                bauth.setCreatedTime(new Date());
                bauth.setExpiresIn(tokenExpireIn);
                bauth.setIcon(icon);
                bauth.setNickName(nickName);
                bauth.setOpenId(openID);
                bauth.setRefreshAccessTime(new Date());
                bauth.setType(AuthType.QQ.getCode());
                authService.save(bauth);
                //用户绑定,跳转页面
                model.addAttribute("openId", openID);
                model.addAttribute("loginFrom", AuthType.QQ.getCode());
                return "site/bindAuth.jsp";
            }else{
                // 登录过但因某种原因为绑定账户
                bauth.setAccessToken(accessToken);
                bauth.setExpiresIn(tokenExpireIn);
                bauth.setIcon(icon);
                bauth.setNickName(nickName);
                bauth.setRefreshAccessTime(new Date());
                authService.update(bauth);
                //用户绑定,跳转页面
                model.addAttribute("openId", openID);
                model.addAttribute("loginFrom", AuthType.QQ.getCode());
                return "site/bindAuth.jsp";
            }*/

        } catch (QQConnectException e) {
            e.printStackTrace();
        }
        return "redirect:/index";
    }

    @RequestMapping("/qqss")
    public void talk(HttpServletRequest request, HttpServletResponse response,
            Model model) throws IOException {
        response.setContentType("text/html;charset=utf-8");

        request.setCharacterEncoding("utf-8");
        String con = request.getParameter("con");
        HttpSession session = request.getSession();
        String accessToken = (String) session.getAttribute("demo_access_token");
        String openID = (String) session.getAttribute("demo_openid");
        System.out.println(accessToken);
        System.out.println(openID);
        // 请开发者自行校验获取的con值是否有效
        if (con != "") {
            Topic topic = new Topic(accessToken, openID);
            try {
                GeneralResultBean grb = topic.addTopic(con);
                if (grb.getRet() == 0) {
                    response.getWriter()
                            .println(
                                    "<a href=\"http://www.qzone.com\" target=\"_blank\">您的说说已发表成功,请登录Qzone查看</a>");
                } else {
                    response.getWriter().println(
                            "很遗憾的通知您,发表说说失败!原因: " + grb.getMsg());
                }
            } catch (QQConnectException e) {
                System.out.println("抛异常了?");
            }
        } else {
            System.out.println("获取到的值为空?");
        }
    }
}
QQController
@RequestMapping("/")
@Controller
public class SinaController extends AuthController {

    @Resource
    private IBAuthService authService;

    @Resource
    private IUserService userService;

    @RequestMapping("/sinalogin")
    public void index(HttpServletRequest request, HttpServletResponse response,
            Model model) throws IOException {
        response.setContentType("text/html;charset=utf-8");
        try {
            response.sendRedirect(new Oauth().authorize("code"));
            LOGGER.info("login by weibo");
        } catch (WeiboException e) {
            e.printStackTrace();
        }

    }

    @RequestMapping("/weibo_callback")
    public String callback(HttpServletRequest request,
            HttpServletResponse response, Model model) throws IOException {
        try {
            Oauth oauth = new Oauth();
            String code = request.getParameter("code");
            LOGGER.info("code: " + code);
            AccessToken accessTokenObj = oauth.getAccessTokenByCode(code);
            if (accessTokenObj == null) {
                LOGGER.error("AccessToken 获取失败,code:" + code);
            }
            String accessToken = accessTokenObj.getAccessToken();
            String openId = accessTokenObj.getUID();

            String expireInStr = accessTokenObj.getExpireIn();

            Users um = new Users(accessToken);
            User user = um.showUserById(openId);
            LOGGER.info(user.toString());

            BAuth newAuth = new BAuth();
            newAuth.setAccessToken(accessToken);
            newAuth.setExpiresIn(expireInStr != null ? Long
                    .parseLong(expireInStr) : 3600);
            newAuth.setIcon(user.getAvatarLarge());
            newAuth.setNickName(user.getScreenName());
            newAuth.setOpenId(openId);
            newAuth.setType(AuthType.SINA.getCode());
            return this.callBack(model, newAuth);

        } catch (WeiboException e) {
            if (401 == e.getStatusCode()) {
                LOGGER.error("Unable to get the access token.");
            } else {
                e.printStackTrace();
            }
        }

        return "redirect:/index";
    }

}
SinaController

 

-------------------------------------------------------------------------------------------------------------

页面效果

注:该流程为用户首次使用第三方登录时流程

1,登录页面放置第三方登录图标

2,点击图标接入第三方接口,可跳转至第三方登录界面

3,第三方登录完成,用户账户绑定

4,用户登录后,可在个人设置中管理第三方登录的绑定

 

posted @ 2015-01-11 22:09  china2k  阅读(1918)  评论(0编辑  收藏  举报