Oltu在Jersey框架上实现oauth2.0受权模块

oltu是一个开源的oauth2.0协议的实现,本人在此开源项目的基础上进行修改,实现一个自定义的oauth2.0模块。java

关于oltu的使用你们能够看这里:http://oltu.apache.org/git

项目能够从这里下载:http://mirror.bit.edu.cn/apache/oltu/org.apache.oltu.oauth2/spring

项目中我将四种受权方式都作了实现(受权码模式、简化模式、密码模式及客户端模式),可是这里仅以受权码模式为例,服务端采用Jersey框架实现,而客户端采用spring mvc实现。sql

图片1

服务器端实现:为了方便开发,我将oltu的全部源码都拖进了项目中,而不是导入jar,由于不少地方可能在我所开发的项目中不适用,这样能够方便修改和跟踪代码。express

其实服务端开发很简单,主要集中在两个比较主要的文件中,一个是请求受权的AuthzEndpoint.java,一个是生成令牌的TokenEndpoint.java,如图所示。apache

code

至于资源控制器因为我开发的项目中,资源访问控制是采用过滤器的方式,所以没有用到oltu提供的java类,两个主要类文件的代码修改以下:json

AuthzEndpoint.java服务器

     /**
     *       Copyright 2010 Newcastle University
     *
     *          http://research.ncl.ac.uk/smart/
     *
     * Licensed to the Apache Software Foundation (ASF) under one or more
     * contributor license agreements.  See the NOTICE file distributed with
     * this work for additional information regarding copyright ownership.
     * The ASF licenses this file to You under the Apache License, Version 2.0
     * (the "License"); you may not use this file except in compliance with
     * the License.  You may obtain a copy of the License at
     *
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
     
     package org.apache.oltu.oauth2.integration.endpoints;
     
     import java.net.URI;
     import java.net.URISyntaxException;
     import java.text.SimpleDateFormat;
     import java.util.HashMap;
     import java.util.Map;
     import java.util.Properties;
     
     import javax.servlet.http.HttpServletRequest;
     import javax.servlet.http.HttpServletResponse;
     import javax.ws.rs.GET;
     import javax.ws.rs.Path;
     import javax.ws.rs.core.Context;
     import javax.ws.rs.core.Response;
     
     import org.apache.ibatis.session.SqlSession;
     import org.apache.oltu.oauth2.as.issuer.MD5Generator;
     import org.apache.oltu.oauth2.as.issuer.OAuthIssuerImpl;
     import org.apache.oltu.oauth2.as.request.OAuthAuthzRequest;
     import org.apache.oltu.oauth2.as.response.OAuthASResponse;
     import org.apache.oltu.oauth2.common.OAuth;
     import org.apache.oltu.oauth2.common.error.OAuthError;
     import org.apache.oltu.oauth2.common.error.ServerErrorType;
     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.ResponseType;
     import org.apache.oltu.oauth2.integration.utils.Cache;
     import org.apache.oltu.oauth2.integration.utils.CacheManager;
     
     import com.cz.bean.App;
     import com.cz.bean.Authority;
     import com.cz.bean.RefreshToken;
     import com.cz.dao.AppMapper;
     import com.cz.dao.AuthorityMapper;
     import com.cz.dao.RefreshTokenMapper;
     import com.cz.util.DbUtil;
     
     /**
     *
     * client request authorization
     *
     */
     @Path ( "/authz" )
     public class AuthzEndpoint {
         SqlSession sqlSession = DbUtil.getSessionFactory().openSession( true );
         AppMapper appDao = sqlSession.getMapper(AppMapper. class );
         AuthorityMapper authorityDao = sqlSession.getMapper(AuthorityMapper. class );
         RefreshTokenMapper refreshTokenDao = sqlSession
                 .getMapper(RefreshTokenMapper. class );
         
        //登陆页面
        private static String loginPage;
         
        //错误页面
        private static String errorPage;
         
         static {
            Properties p = new Properties();
            try {
                p.load(AuthzEndpoint. class .getClassLoader().getResourceAsStream(
                        "config.properties" ));
                loginPage = p.getProperty( "loginPage" );
                errorPage = p.getProperty( "errorPage" );
            } catch (Exception e) {
                e.printStackTrace();
            }
         
         }
     
         public static final String INVALID_CLIENT_DESCRIPTION = "Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method)." ;
     
         @GET
         public Response authorize( @Context HttpServletRequest request)
                 throws URISyntaxException, OAuthSystemException {
     
             OAuthAuthzRequest oauthRequest = null ;
     
             OAuthIssuerImpl oauthIssuerImpl = new OAuthIssuerImpl(
                     new MD5Generator());
     
             try {
                 oauthRequest = new OAuthAuthzRequest(request);
                 
                 /*
                 * 当前登陆的用户,模拟一个从session中获取的登陆用户
                 * 该方法未实现,待模块与养老平台整合时,应调用养老平台方法判断用户是否已登陆
                 * 并得到对应用户的userId
                 */
                 String userId = "1" ;
     
                 if ( "" .equals(userId) || userId == null ) {
                     // 用户没有登陆就跳转到登陆页面
                     return Response.temporaryRedirect( new URI(loginPage)).build();
                 }
     
                 App app = null ;
                 if (oauthRequest.getClientId()!= null && ! "" .equals(oauthRequest.getClientId())){
                     app = appDao.selectByPrimaryKey(oauthRequest.getClientId());
                 } else {
                     return Response.temporaryRedirect( new URI(errorPage+ "?error=" +ServerErrorType.CLIENT_ID_IS_NULL)).build();
                 }
                 
                 // 根据response_type建立response
                 String responseType = oauthRequest
                         .getParam(OAuth.OAUTH_RESPONSE_TYPE);
     
                 OAuthASResponse.OAuthAuthorizationResponseBuilder builder = OAuthASResponse
                         .authorizationResponse(request,
                                 HttpServletResponse.SC_FOUND);
                 
                 // 检查传入的客户端id是否正确
                 if (app == null ) {
                     return Response.temporaryRedirect( new URI(errorPage+ "?error=" +ServerErrorType.UNKOWN_CLIENT_ID)).build();
                 }
     
                 String scope = oauthRequest.getParam(OAuth.OAUTH_SCOPE);
                 
                 // 受权请求类型
                 if (responseType.equals(ResponseType.CODE.toString())) {
                     String code = oauthIssuerImpl.authorizationCode();
                     builder.setCode(code);
                     CacheManager.putCache(userId+ "_code" , new Cache( "code" , code,
                             216000000 , false ));
                     CacheManager.putCache(userId+ "_scope" , new Cache( "scope" , scope,
                             216000000 , false ));
                 }
                 if (responseType.equals(ResponseType.TOKEN.toString())) {
                     // 校验client_secret
                     if (!app.getSecret_key().equals(oauthRequest.getClientSecret())) {
                            OAuthResponse response =
                                    OAuthASResponse.errorResponse(HttpServletResponse.SC_OK)
                                        .setError(OAuthError.TokenResponse.INVALID_CLIENT).setErrorDescription(INVALID_CLIENT_DESCRIPTION)
                                        .buildJSONMessage();
                                return Response.status(response.getResponseStatus()).entity(response.getBody()).build();
                     }
                     String accessToken = oauthIssuerImpl.accessToken();
                     builder.setAccessToken(accessToken);
                     builder.setExpiresIn(3600l);
                    //判断是否已经受权----待调整是放在authz部分仍是token部分
                    Map<String,Object> aQueryParam = new HashMap<>();
                    aQueryParam.put( "appKey" ,oauthRequest.getClientId());
                    aQueryParam.put( "userId" ,Integer.valueOf(userId));
                    if (authorityDao.findUnique(aQueryParam)== null ){
                         Authority authority = new Authority();
                        authority.setApp_key(oauthRequest.getClientId());
                        authority.setUser_id(Integer.valueOf(userId));
                        authorityDao.insert(authority);
                    }
                     // 存储token,已受权则更新令牌,未受权则新增令牌
                     Map<String,Object> rQueryParam = new HashMap<>();
                     rQueryParam.put( "appKey" , oauthRequest.getClientId());
                     rQueryParam.put( "userId" , Integer.valueOf(userId));
                     if (refreshTokenDao.findUnique(rQueryParam) != null ) {
                         Map<String,Object> map = new HashMap<>();
                         map.put( "accessToken" , accessToken);
                         map.put( "appKey" , oauthRequest.getClientId());
                         map.put( "userId" , Integer.valueOf(userId));
                         map.put( "createTime" , getDate());
                         map.put( "scope" , scope);
                         map.put( "authorizationTime" , getDate());
                         refreshTokenDao.updateAccessToken(map);
                     } else {
                         RefreshToken rt = new RefreshToken();
                         rt.setApp_key(oauthRequest.getClientId());
                         rt.setUser_id(Integer.valueOf(userId));
                         rt.setAccess_token(accessToken);
                         rt.setCreate_time(getDate());
                         rt.setAuthorization_time(getDate());
                         rt.setExpire( "3600" );
                         rt.setScope(scope);
                         rt.setAuthorization_time(getDate());
                         refreshTokenDao.insert(rt);
                     }
                 }
                 
                 // 客户端跳转URI
                 String redirectURI = oauthRequest
                         .getParam(OAuth.OAUTH_REDIRECT_URI);
     
                 final OAuthResponse response = builder.location(redirectURI).setParam( "scope" , scope)
                         .buildQueryMessage();
                 String test = response.getLocationUri();
                 URI url = new URI(response.getLocationUri());
     
                 return Response.status(response.getResponseStatus()).location(url)
                         .build();
     
             } catch (OAuthProblemException e) {
                 return Response.temporaryRedirect( new URI(errorPage+ "?error=" +ServerErrorType.BAD_RQUEST)).build();
             }
         }
         
         private String getDate() {
             SimpleDateFormat sdf = new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss" );
             return sdf.format(System.currentTimeMillis());
         }
     }

上面的代码是受权码认证的第一步,当用户赞成受权以后向服务器请求受权码。你可使用一下腾讯的受权功能来加深一下体会,由于我所开发的模块也是参考腾讯的受权认证流程来实现的,客户端经过提交请求,访问相似http://192.168.19.75:10087/oauth/authz?client_id=s6BhdRkqt3&client_secret=12345&redirect_uri=http://localhost:8080/redirect.jsp&state=y&response_type=authorization_code的连接来访问上面的程序,参数的含义以下session

client_id :客户端idmvc

client_secret:客户端密钥

redirect_uri:回调地址,第三方应用定义的地址

State:状态,服务器将返回一个如出一辙的参数。

response_type:受权方式,这里必须是authorization_code,表示受权码    方式。

这个过程结束时,服务器会跳转至第三方应用定义的回调地址并附上受权码,而第三方经过这个回调地址得到受权码并进行相应的处理,而这个过程在oltu的实现中其实就是几行简单的代码:

     // 建立response wrapper
     OAuthAuthzResponse oar = null ;
     oar = OAuthAuthzResponse.oauthCodeAuthzResponse(request);
     
     // 得到受权码
     String code = oar.getCode();

上面的代码就是oltu客户端接收服务器发回的受权码的代码,其中request是一个HttpServletRequest对象,得到了受权码以后,按照下一步的流程,天然就是向受权服务器请求令牌并附上上一步得到的受权码。服务器得到受权码并进行相应处理的代码以下:

TokenEndpoint.java

     /**
     *       Copyright 2010 Newcastle University
     *
     *          http://research.ncl.ac.uk/smart/
     *
     * Licensed to the Apache Software Foundation (ASF) under one or more
     * contributor license agreements.  See the NOTICE file distributed with
     * this work for additional information regarding copyright ownership.
     * The ASF licenses this file to You under the Apache License, Version 2.0
     * (the "License"); you may not use this file except in compliance with
     * the License.  You may obtain a copy of the License at
     *
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
     
     package org.apache.oltu.oauth2.integration.endpoints;
     
     import java.net.URI;
     import java.net.URISyntaxException;
     import java.text.SimpleDateFormat;
     import java.util.HashMap;
     import java.util.Map;
     import java.util.Properties;
     
     import javax.servlet.http.HttpServletRequest;
     import javax.servlet.http.HttpServletResponse;
     import javax.ws.rs.Consumes;
     import javax.ws.rs.POST;
     import javax.ws.rs.Path;
     import javax.ws.rs.Produces;
     import javax.ws.rs.core.Context;
     import javax.ws.rs.core.Response;
     
     import org.apache.ibatis.session.SqlSession;
     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.OAuthTokenRequest;
     import org.apache.oltu.oauth2.as.response.OAuthASResponse;
     import org.apache.oltu.oauth2.common.OAuth;
     import org.apache.oltu.oauth2.common.error.OAuthError;
     import org.apache.oltu.oauth2.common.error.ServerErrorType;
     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.integration.utils.CacheManager;
     
     import com.cz.bean.App;
     import com.cz.bean.Authority;
     import com.cz.bean.RefreshToken;
     import com.cz.bean.User;
     import com.cz.dao.AppMapper;
     import com.cz.dao.AuthorityMapper;
     import com.cz.dao.RefreshTokenMapper;
     import com.cz.dao.UserMapper;
     import com.cz.util.DbUtil;
     
     /**
     *
     * get access token
     *
     */
     @Path ( "/token" )
     public class TokenEndpoint {
         SqlSession sqlSession = DbUtil.getSessionFactory().openSession( true );
         AppMapper appDao = sqlSession.getMapper(AppMapper. class );
         RefreshTokenMapper refreshTokenDao = sqlSession
                 .getMapper(RefreshTokenMapper. class );
         UserMapper dao = sqlSession.getMapper(UserMapper. class );
         AuthorityMapper authorityDao = sqlSession.getMapper(AuthorityMapper. class );
     
         // 登陆页面
         private static String loginPage;
     
         // 错误页面
         private static String errorPage;
     
         static {
             Properties p = new Properties();
             try {
                 p.load(AuthzEndpoint. class .getClassLoader().getResourceAsStream(
                         "config.properties" ));
                 loginPage = p.getProperty( "loginPage" );
                 errorPage = p.getProperty( "errorPage" );
             } catch (Exception e) {
                 e.printStackTrace();
             }
     
         }
     
         public static final String INVALID_CLIENT_DESCRIPTION = "Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method)." ;
     
         @SuppressWarnings ({ "unchecked" , "rawtypes" })
         @POST
         @Consumes ( "application/x-www-form-urlencoded" )
         @Produces ( "application/json" )
         public Response authorize( @Context HttpServletRequest request)
                 throws OAuthSystemException, URISyntaxException {
     
             OAuthTokenRequest oauthRequest = null ;
             String scope = "" ;
             OAuthIssuer oauthIssuerImpl = new OAuthIssuerImpl( new MD5Generator());
     
             try {
                 oauthRequest = new OAuthTokenRequest(request);
     
                 /*
                 * 当前登陆的用户,模拟一个从session中获取的登陆用户
                 * 该方法未实现,待模块与养老平台整合时,应调用养老平台方法判断用户是否已登陆
                 */
                 String userId = "1" ;
     
                 if ( "" .equals(userId) || userId == null ) {
                     // 用户没有登陆的话就跳转到登陆页面
                     return Response.temporaryRedirect( new URI(loginPage)).build();
                 }
     
                 App app = null ;
                 if (oauthRequest.getClientId() != null && ! "" .equals(oauthRequest.getClientId())) {
                     app = appDao.selectByPrimaryKey(oauthRequest.getClientId());
                 } else {
                     return Response.temporaryRedirect( new URI(errorPage + "?error=" + ServerErrorType.CLIENT_ID_IS_NULL)).build();
                 }
     
                 // 校验clientid
                 if (app == null || !app.getApp_key().toString().equals(oauthRequest.getClientId())) {
                     if (oauthRequest.getParam(OAuth.OAUTH_GRANT_TYPE).equals(GrantType.AUTHORIZATION_CODE.toString())){
                         return Response.temporaryRedirect( new URI(errorPage + "?error=" + ServerErrorType.UNKOWN_CLIENT_ID)).build();
                     } else {
                        OAuthResponse response =
                                OAuthASResponse.errorResponse(HttpServletResponse.SC_OK)
                                    .setError(OAuthError.TokenResponse.INVALID_CLIENT).setErrorDescription(INVALID_CLIENT_DESCRIPTION)
                                    .buildJSONMessage();
                            return Response.status(response.getResponseStatus()).entity(response.getBody()).build();
                     }
                 }
     
                 // 校验client_secret
                 if (!app.getSecret_key().equals(oauthRequest.getClientSecret())) {
                     if (oauthRequest.getParam(OAuth.OAUTH_GRANT_TYPE).equals(GrantType.AUTHORIZATION_CODE.toString())){
                         return Response.temporaryRedirect( new URI(errorPage + "?error=" + ServerErrorType.UNKOWN_CLIENT_SECRET)).build();
                     } else {
                        OAuthResponse response =
                                OAuthASResponse.errorResponse(HttpServletResponse.SC_OK)
                                    .setError(OAuthError.TokenResponse.INVALID_CLIENT).setErrorDescription(INVALID_CLIENT_DESCRIPTION)
                                    .buildJSONMessage();
                            return Response.status(response.getResponseStatus()).entity(response.getBody()).build();
                     }
                 }
     
                 // 校验不一样类型的受权方式
                 if (oauthRequest.getParam(OAuth.OAUTH_GRANT_TYPE).equals(GrantType.AUTHORIZATION_CODE.toString())) {
                     String cacheCode = null ;
                     if (CacheManager.getCacheInfo(userId + "_code" ).getValue() != null ) {
                         cacheCode = CacheManager.getCacheInfo(userId + "_code" )
                                 .getValue().toString();
                     } else {
                         // 用户没有登陆的话就跳转到登陆页面
                         return Response.temporaryRedirect( new URI(loginPage)).build();
                     }
                     
                     if (!cacheCode.equals(oauthRequest.getParam(OAuth.OAUTH_CODE))) {
                         return Response.temporaryRedirect( new URI(errorPage+ "?error=" + ServerErrorType.INVALID_AUTHORIZATION_CODE)).build();
                     }
                     
                    if (CacheManager.getCacheInfo(userId+ "_scope" ).getValue()!= null ){
                         scope = CacheManager.getCacheInfo(userId+ "_scope" ).getValue().toString();
                    }
                 } else if (oauthRequest.getParam(OAuth.OAUTH_GRANT_TYPE).equals(GrantType.PASSWORD.toString())) {
                     User user = dao.getById(userId);
                     if (!user.getPassword().equals(oauthRequest.getPassword())|| !user.getName().equals(oauthRequest.getUsername())) {
                        OAuthResponse response = OAuthASResponse
                                .errorResponse(HttpServletResponse.SC_OK)
                                .setError(OAuthError.TokenResponse.INVALID_CLIENT)
                                .setErrorDescription( "Invalid username or password." )
                                .buildJSONMessage();
                            return Response.status(response.getResponseStatus()).entity(response.getBody()).build();
                     }
                 } else if (oauthRequest.getParam(OAuth.OAUTH_GRANT_TYPE).equals(
                         GrantType.CLIENT_CREDENTIALS.toString())) {
                     // 客户端id以及secret已验证,更多验证规则在这里添加,没有其余验证则程序直接发放令牌
     //                OAuthResponse response = OAuthASResponse
     //                        .errorResponse(HttpServletResponse.SC_OK)
     //                        .setError(OAuthError.TokenResponse.INVALID_GRANT)
     //                        .setErrorDescription("invalid client")
     //                        .buildJSONMessage();
     //                    return Response.status(response.getResponseStatus()).entity(response.getBody()).build();
                     
                 } else if (oauthRequest.getParam(OAuth.OAUTH_GRANT_TYPE).equals(
                         GrantType.REFRESH_TOKEN.toString())) {
                     // 刷新令牌未实现
                 }
     
                 String accessToken = oauthIssuerImpl.accessToken();
                 String refreshToken = oauthIssuerImpl.refreshToken();
                 // 构建响应
                 OAuthResponse response = OAuthASResponse
                         .tokenResponse(HttpServletResponse.SC_OK)
                         .setAccessToken(accessToken).setRefreshToken(refreshToken)
                         .setExpiresIn( "3600" )
                         .buildJSONMessage();
     
                 // 判断是否已经受权----待调整是放在authz部分仍是token部分
                 Map aQueryParam = new HashMap();
                 aQueryParam.put( "appKey" , oauthRequest.getClientId());
                 aQueryParam.put( "userId" , Integer.valueOf(userId));
                 if (authorityDao.findUnique(aQueryParam) == null ) {
                     Authority authority = new Authority();
                     authority.setApp_key(oauthRequest.getClientId());
                     authority.setUser_id(Integer.valueOf(userId));
                     authorityDao.insert(authority);
                 }
     
     //            String scope = "";
     //            if(CacheManager.getCacheInfo(userId+"_scope").getValue()!=null){
     //                scope = CacheManager.getCacheInfo(userId+"_scope").getValue().toString();
     //            }
                 
                 // 存储token,已受权则更新令牌,未受权则新增令牌
                 Map rQueryParam = new HashMap();
                 rQueryParam.put( "appKey" , oauthRequest.getClientId());
                 rQueryParam.put( "userId" , Integer.valueOf(userId));
                 if (refreshTokenDao.findUnique(rQueryParam) != null ) {
                     Map map = new HashMap();
                     map.put( "accessToken" , accessToken);
                     map.put( "appKey" , oauthRequest.getClientId());
                     map.put( "userId" , Integer.valueOf(userId));
                     map.put( "createTime" , getDate());
                     map.put( "scope" , scope);
                     map.put( "authorizationTime" , getDate());
                     refreshTokenDao.updateAccessToken(map);
                 } else {
                     RefreshToken rt = new RefreshToken();
                     rt.setApp_key(oauthRequest.getClientId());
                     rt.setUser_id(Integer.valueOf(userId));
                     rt.setAccess_token(accessToken);
                     rt.setRefresh_token(refreshToken);
                     rt.setCreate_time(getDate());
                     rt.setAuthorization_time(getDate());
                     rt.setExpire( "3600" );
                     rt.setScope(scope);
                     rt.setAuthorization_time(getDate());
                     refreshTokenDao.insert(rt);
                 }
     
                 return Response.status(response.getResponseStatus())
                         .entity(response.getBody()).build();
     
             } catch (OAuthProblemException e) {
                 System.out.println(e.getDescription());
                 return Response.temporaryRedirect( new URI(errorPage + "?error=" + ServerErrorType.BAD_RQUEST)).build();
             }
         }
     
         private String getDate() {
             SimpleDateFormat sdf = new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss" );
             return sdf.format(System.currentTimeMillis());
         }
     }

上面的代码,处理了客户端发来的申请令牌请求,并向客户端发放访问令牌,而oltu的客户端则经过以下代码来完成这个请求令牌和解析令牌的过程:

     OAuthClient client = new OAuthClient( new URLConnectionClient());
     OAuthAccessTokenResponse oauthResponse = null ;
     oauthResponse =   
             client.accessToken(request, OAuth.HttpMethod.POST);  
     String token = oauthResponse.getRefreshToken();

若是你是第一次开发,oauth2.0的认证过程可能会让你以为头疼,由于你首先须要对这个流程很熟悉,而且同时要看懂了oltu的代码才好理解这个开源的项目究竟是怎么实现这个过程的,所以这里我不过多的粘贴代码,由于这并无什么卵用,仍是运行项目和追踪代码比较容易理解它的原理,下面是我实现的项目代码,代码写得比较简陋,不过对于跟我同样的菜鸟,仍是能起到必定的帮助的~

服务端:https://git.oschina.net/honganlei/oauth-server.git

服务端受权登陆页面:https://git.oschina.net/honganlei/OauthClient.git

第三方接入受权模块的例子:https://git.oschina.net/honganlei/OauthApp.git

相关文章
相关标签/搜索