Sping+SpringMvc+Mybatis+OAuth2.0服务器与客户端实现(第三方登录)(二)
接续上一部分,过了个年,忘得差不多了,正好记录整理一下。
简要说明下。oauth的原理不做详细介绍,可自行百度。
简单步骤总结。
客户端部分:
1、客户端向服务端请求Code,请求中携带着部分必须信息。
2、客户端的回调中接收服务端产生的Code,继续向服务端请求资源授权AccessToken。
3、客户端得到资源授权后,再去请求对应的资源。
代码如下
package com.yzz.oauthclient.controller;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;import org.apache.oltu.oauth2.client.OAuthClient;
import org.apache.oltu.oauth2.client.URLConnectionClient;
import org.apache.oltu.oauth2.client.request.OAuthBearerClientRequest;
import org.apache.oltu.oauth2.client.request.OAuthClientRequest;
import org.apache.oltu.oauth2.client.response.OAuthAccessTokenResponse;
import org.apache.oltu.oauth2.client.response.OAuthResourceResponse;
import org.apache.oltu.oauth2.common.OAuth;
import org.apache.oltu.oauth2.common.exception.OAuthProblemException;
import org.apache.oltu.oauth2.common.exception.OAuthSystemException;
import org.apache.oltu.oauth2.common.message.types.GrantType;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
@RequestMapping("/client")
@Controller
public class ClientController {String clientId = null;String clientSecret = null;String accessTokenUrl = null;String userInfoUrl = null;String redirectUrl = null;String response_type = null;String code= null;@RequestMapping("")public String index(){return "index";}//1、客户端向服务端请求Code。参数必须带有在服务数据库中有记录的clientId,回调路径redirectUrl,以及其他参数。 @RequestMapping("/getCode")public String getCode(HttpServletRequest request, HttpServletResponse response){clientId = "test_id1";clientSecret = "test_secret1";//服务器部分controller的名称accessTokenUrl = "checkClientId";//服务器回调客户端的路径redirectUrl = "http://localhost:8088/OauthClient/client/getAccessToken";response_type = "code";OAuthClient oAuthClient =new OAuthClient(new URLConnectionClient());String requestUrl = null;try {//构建oauthd的请求。设置请求服务地址(accessTokenUrl)、clientId、response_type、redirectUrlOAuthClientRequest accessTokenRequest = OAuthClientRequest.authorizationLocation(accessTokenUrl).setResponseType(response_type).setClientId(clientId).setRedirectURI(redirectUrl).buildQueryMessage();requestUrl = accessTokenRequest.getLocationUri();System.out.println("发送给OAuth服务器部分路径:"+requestUrl);} catch (Exception e) {e.printStackTrace(); }//向服务器进行请求return "redirect:http://localhost:8088/OauthServer/simpleDemo/"+requestUrl ;}@RequestMapping("/error")public String error(){return "error";}//2、客户端的回调中接收服务端产生的Code,继续向服务端请求资源授权AccessToken。@RequestMapping("/getAccessToken")public String getAccessToken(HttpServletRequest request){clientId = "test_id1";clientSecret = "test_secret1";accessTokenUrl="http://localhost:8088/OauthServer/simpleDemo/createAccessToken";redirectUrl = "http://localhost:8088/OauthClient/client/getResouce";HttpServletRequest httpRequest = (HttpServletRequest)request;code = httpRequest.getParameter("code");System.out.println("收到服务器传递过来的code:"+code);OAuthClient oAuthClient =new OAuthClient(new URLConnectionClient());try {OAuthClientRequest accessTokenRequest = OAuthClientRequest.tokenLocation(accessTokenUrl).setGrantType(GrantType.AUTHORIZATION_CODE).setClientId(clientId).setClientSecret(clientSecret).setCode(code).setRedirectURI(redirectUrl).buildQueryMessage();//去服务端请求access token,并返回响应System.out.println("访问服务器路径:"+accessTokenRequest.getLocationUri());OAuthAccessTokenResponse oAuthResponse =oAuthClient.accessToken(accessTokenRequest, OAuth.HttpMethod.POST);System.out.println(oAuthResponse);//获取服务端返回过来的access tokenString accessToken = oAuthResponse.getAccessToken();//查看access token是否过期Long expiresIn =oAuthResponse.getExpiresIn();System.out.println("客户端得到服务器部分产生的token:::"+accessToken);return"redirect:http://localhost:8088/OauthClient/client/getResouce?accessToken="+accessToken;} catch (Exception e) {e.printStackTrace();}return null;}//3、客户端得到资源授权后,再去请求对应的资源。@RequestMapping("/getResouce")public ModelAndView getResouce(String accessToken){System.out.println("客户端收到的访问令牌:"+accessToken);//查询服务器用户信息的controlleruserInfoUrl = "http://localhost:8088/OauthServer/simpleDemo/userInfo";//构建oauthOAuthClient oAuthClient =new OAuthClient(new URLConnectionClient());OAuthClientRequest userInfoRequest;try {userInfoRequest = new OAuthBearerClientRequest(userInfoUrl).setAccessToken(accessToken).buildQueryMessage();OAuthResourceResponse resourceResponse =oAuthClient.resource(userInfoRequest, OAuth.HttpMethod.GET, OAuthResourceResponse.class);String username = resourceResponse.getBody();System.out.println("获取的用户信息:"+username);ModelAndView modelAndView =new ModelAndView("userInfoPage");modelAndView.addObject("userName",username);modelAndView.setViewName("index");return modelAndView;} catch (OAuthSystemException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (OAuthProblemException e) {// TODO Auto-generated catch blocke.printStackTrace();}return null;}
}
index.jsp
<%@ page language="java" contentType="text/html; charset=utf-8"pageEncoding="utf-8"%><%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
OAUTH CLIENT
this is index jsp page
userName is:
${userName}
客户端比较简陋,有需要的话可以继续扩展,只有一个Controller和一个index.jsp,结构图如下:
1、接收客户端的请求,对部分参数进行验证,验证通过后产生Code,回调客户端请求中的回调路径。
2、接收客户端的请求,创建资源请求许可。
3、资源管理部分对客户端的请求进行许可验证,通过后才返回对应的资源信息。
package com.yzz.oauthserver.controller;import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;import org.apache.oltu.oauth2.as.issuer.MD5Generator;
import org.apache.oltu.oauth2.as.issuer.OAuthIssuer;
import org.apache.oltu.oauth2.as.issuer.OAuthIssuerImpl;
import org.apache.oltu.oauth2.as.request.OAuthAuthzRequest;
import org.apache.oltu.oauth2.as.request.OAuthTokenRequest;
import org.apache.oltu.oauth2.as.response.OAuthASResponse;
import org.apache.oltu.oauth2.common.error.OAuthError;
import org.apache.oltu.oauth2.common.exception.OAuthProblemException;
import org.apache.oltu.oauth2.common.exception.OAuthSystemException;
import org.apache.oltu.oauth2.common.message.OAuthResponse;
import org.apache.oltu.oauth2.common.message.types.GrantType;
import org.apache.oltu.oauth2.common.message.types.ParameterStyle;
import org.apache.oltu.oauth2.common.message.types.ResponseType;
import org.apache.oltu.oauth2.rs.request.OAuthAccessResourceRequest;
import org.apache.oltu.oauth2.rs.response.OAuthRSResponse;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;import com.yzz.oauthserver.bean.User;
import com.yzz.oauthserver.services.Cache;
import com.yzz.oauthserver.services.ClientService;
import com.yzz.oauthserver.services.UserService;
import com.yzz.oauthserver.tool.Constants;@RequestMapping({"/simpleDemo"})
@Controller
public class SimpleDemo
{@Resourceprivate ClientService client;@Resource private UserService userService;@Resourceprivate Cache cache = null;//1、接收客户端的请求,对部分参数进行验证,验证通过后产生Code,回调客户端请求中的回调路径@RequestMapping("/checkClientId")public ModelAndView checkClientId(HttpServletRequest request){ModelAndView m=new ModelAndView();OAuthAuthzRequest oauthRequest;String responseType=null,redirectUri=null,clientId=null;try {oauthRequest = new OAuthAuthzRequest(request);responseType=oauthRequest.getResponseType();redirectUri=oauthRequest.getRedirectURI();clientId=oauthRequest.getClientId();//数据库查询client_id判断。if (client.checkClient(clientId)<1) {OAuthResponse response =OAuthASResponse.errorResponse(HttpServletResponse.SC_BAD_REQUEST).setError(OAuthError.TokenResponse.INVALID_CLIENT).setErrorDescription(Constants.INVALID_CLIENT_DESCRIPTION).buildJSONMessage();//定向到客户端的错误页面,待补充return new ModelAndView("http://localhost:8088/OauthClient/client/error","massage","错误页面,待补充");}} catch (OAuthSystemException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (OAuthProblemException e) {// TODO Auto-generated catch blocke.printStackTrace();}//跳转到用户登录页面return new ModelAndView("login","url","createCode?response_type="+responseType+"&redirect_uri="+redirectUri+"&client_id="+clientId);}@RequestMapping(value="/createCode",produces = "text/html;charset=UTF-8")public String createCode(HttpServletRequest request) {try{String userName=request.getParameter("userName");String userPassword=request.getParameter("userPassword");User user=userService.Login(userName, userPassword);if(user!=null){//登录成功OAuthAuthzRequest oauthRequest = new OAuthAuthzRequest(request);String authorizationCode = null;String responseType = oauthRequest.getParam("response_type");if (responseType.equals(ResponseType.CODE.toString())){OAuthIssuerImpl oauthIssuerImpl = new OAuthIssuerImpl(new MD5Generator());authorizationCode = oauthIssuerImpl.authorizationCode();System.out.println("生成的code是:" + authorizationCode);//将用户信息缓存到code中this.cache.setUser(authorizationCode, user);}else{System.out.println(user.getUserName());//类型错误return "error";}OAuthASResponse.OAuthAuthorizationResponseBuilder builder = OAuthASResponse.authorizationResponse(request, 302);builder.setCode(authorizationCode);String redirectURI = oauthRequest.getParam("redirect_uri");System.out.println("构建响应,回调地址是:"+redirectURI);OAuthResponse response = builder.location(redirectURI).buildQueryMessage();String responseUri = response.getLocationUri();System.out.println("服务端重定向到客户端的路径:" + responseUri);return "redirect:" + responseUri;}else{System.out.println("账号密码错误");request.setAttribute("msg", "账号密码错误");//账号密码错误return "error";}}catch (OAuthSystemException e) {e.printStackTrace();}catch (OAuthProblemException e) {e.printStackTrace();}return null;}//2、接收客户端的请求,创建资源请求许可@RequestMapping({"/createAccessToken"})public HttpEntity createAccessToken(HttpServletRequest request){System.out.println("进入创建createAccessToken部分");try{OAuthTokenRequest oauthRequest = new OAuthTokenRequest(request);OAuthResponse response;if (client.check(oauthRequest.getClientId(),oauthRequest.getClientSecret())<1) {System.out.println("clientId或ClientSecret不对,挂了");response = OAuthASResponse.errorResponse(401).setError("unauthorized_client").setErrorDescription("客户端验证失败,如错误的client_id/client_secret。").buildJSONMessage();return new ResponseEntity(response.getBody(), HttpStatus.valueOf(response.getResponseStatus()));}String authCode = oauthRequest.getParam("code");System.out.println("参数输出开始==========");System.out.println("客户端传递过来的authCode:" + authCode);System.out.println("传递过来 的 AUTHORIZATION_CODE:" + oauthRequest.getParam("grant_type"));System.out.println("GrantType 的 AUTHORIZATION_CODE:" + GrantType.AUTHORIZATION_CODE.toString());System.out.println("服务器缓存中的用户信息:" + this.cache.getUser(authCode).getUserName());System.out.println("参数输出结束==========");if ((oauthRequest.getParam("grant_type").equals(GrantType.AUTHORIZATION_CODE.toString())) && (this.cache.getUser(authCode) ==null)) {System.out.println("缓存为空,挂了:" + authCode);OAuthResponse response1 =OAuthASResponse.errorResponse(400).setError("invalid_grant").setErrorDescription("错误的授权码").buildJSONMessage();return new ResponseEntity(response1.getBody(), HttpStatus.valueOf(response1.getResponseStatus()));}OAuthIssuer oauthIssuerImpl = new OAuthIssuerImpl(new MD5Generator());String accessToken = oauthIssuerImpl.accessToken();System.out.println("服务端生成的accessToken:" + accessToken);//将用户信息缓存到令牌中this.cache.setUser(accessToken, this.cache.getUser(authCode));OAuthResponse response1 = OAuthASResponse.tokenResponse(200).setAccessToken(accessToken).buildJSONMessage();System.out.println("返回体:" + response1.getBody());return new ResponseEntity(response1.getBody(), HttpStatus.valueOf(response1.getResponseStatus()));}catch (OAuthSystemException e){e.printStackTrace();}catch (OAuthProblemException e) {e.printStackTrace();}return null;}//3、资源管理部分对客户端的请求进行许可验证,通过后才返回对应的资源信息@RequestMapping({"/userInfo"})public HttpEntity getUserInfo(HttpServletRequest request){System.out.println("进入获取资源部分");try{OAuthAccessResourceRequest oauthRequest = new OAuthAccessResourceRequest(request, new ParameterStyle[] { ParameterStyle.QUERY });String accessToken = oauthRequest.getAccessToken();System.out.println("服务端收到客户端传递的accessToken:" + accessToken);System.out.println("缓存中的用户信息:" + this.cache.getUser(accessToken).getUserName());if (this.cache.getUser(accessToken)==null){System.out.println("accessToken缓存中不存在用户信息,挂了");OAuthResponse oauthResponse =OAuthRSResponse.errorResponse(401).setRealm(Constants.RESOURCE_SERVER_NAME).setError("invalid_token").buildHeaderMessage();HttpHeaders headers = new HttpHeaders();headers.add("WWW-Authenticate", oauthResponse.getHeader("WWW-Authenticate"));return new ResponseEntity(headers, HttpStatus.UNAUTHORIZED);}//得到缓存在令牌中的用户信息String userName=this.cache.getUser(accessToken).getUserName();System.out.println("返回给客户端的用户名称:" + userName);return new ResponseEntity(userName, HttpStatus.OK);}catch (OAuthSystemException e) {e.printStackTrace();}catch (OAuthProblemException e) {e.printStackTrace();}return null;}
}
相比客户端,服务端结构丰富一点点,如下图。
服务端主要部分代码:
缓存实现类
package com.yzz.oauthserver.impl;import org.apache.oltu.oauth2.as.issuer.MD5Generator;
import org.apache.oltu.oauth2.as.issuer.OAuthIssuerImpl;
import org.apache.oltu.oauth2.common.exception.OAuthSystemException;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;import com.yzz.oauthserver.bean.User;
import com.yzz.oauthserver.services.Cache;
@Servicepublic class CacheImpl implements Cache{@Cacheable(value="userCache",key="#code")public User setUser(String code, User user) {// TODO Auto-generated method stubreturn user;}@Cacheable(value="userCache",key="#code")public User getUser(String code){return null;}}
其中db.properties、log4j.properties、spring-mvc.xml、spring-mybatis.xml 的配置和第一篇的项目搭建是一样自己改一下对应的配置路径就好。其余的bean、dao和对应impl都比较简单就不列了。sql部分也在第一篇中列举了,比较简单不在重复列举。
路径跳转步骤简介(发起部分为红色,跳转部分为蓝色,调用部分为青色):
1、浏览器输入 http://localhost:8088/OauthClient/client 跳转到客户端 index.jsp页面。点击登录。
2、点击登录跳转到服务器进行clientId部分验证
发起controller:客户端 http://localhost:8088/OauthClient/client/getCode
跳转controller:服务器 http://localhost:8088/OauthServer/simpleDemo/checkClientId
3、验证正确后跳转到服务端的登录界面 (如上图)
发起controller:服务器 http://localhost:8088/OauthServer/simpleDemo/checkClientId
跳转页面:服务端 login.jsp 收集用户登录信息。
跳转controller:服务端 http://localhost:8088/OauthServer/simpleDemo/createCode
4、进行登录验证成功后创建Code,失败着跳转到错误页面
发起controller:服务端 http://localhost:8088/OauthServer/simpleDemo/createCode
失败:跳转页面 :服务器 error.jsp
成功: 跳转controller:客户端 http://localhost:8088/OauthClient/client/getAccessToken
4、客户端获取服务器的资源授权 (后台进行无法截图)
发起controller:客户端 http://localhost:8088/OauthClient/client/getAccessToken
调用controller(内部调用直接得到结果,可看成ajax或者调用内部接口,并不进行页面跳转):
服务器 http://localhost:8088/OauthServer/simpleDemo/createAccessToken
5、得到授权后定向到获取资源部分 (见最后一图)
发起controller:客户端 http://localhost:8088/OauthClient/client/getAccessToken
跳转controller:客户端 http://localhost:8088/OauthClient/client/getResouce
6、带着授权去服务器资源管理部分获取资源 (见最后一图)
发起controller:客户端 http://localhost:8088/OauthClient/client/getResouce
调用controller(同第四步):服务器 http://localhost:8088/OauthServer/simpleDemo/userInfo
7、跳到显示页面
发起controller: 客户端 http://localhost:8088/OauthClient/client/getResouce
跳转页面:客户端 index.jsp。
第7步完成之后实际显示的账户为服务器端所存储的账户信息。也就是通过服务器的账户实现客户端的登录。
正常来说我们实际开发是用不到服务端的,我们需要完善好自己的客户端,再去每一个大的社交平台,类似QQ、微信、微博、百度之类的开通第三方服务。
项目都比较简单,只列出了主要的步骤。各种验证都是简化版本的,有需要的可以自己优化。
源码下载:包含三个简单项目。一个ssm搭建、一个oauth2.0客户端、一个oauth2.0服务端。
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
