原博客地址:http://jinnianshilongnian.iteye.com/blog/2018398
根据下载的pdf学习。
开涛shiro教程-第十七章-OAuth2集成
3.客户端
客户端流程可以参照如很多网站的新浪微博登录功能,或其他的第三方帐号登录功能。
1 客户端进行登录操作 2 跳到oauth2服务端,进行登录授权。成功后,服务端返回auth code。 3 客户端使用auth code去服务器换取access token。 4 客户端根据access token得到用户信息,进行客户端的登录绑定。
(1)POM依赖
此处我们使用 apache oltu oauth2 客户端实现。
1 <dependency> 2 <groupId>org.apache.oltu.oauth2</groupId> 3 <artifactId>org.apache.oltu.oauth2.client</artifactId> 4 <version>0.31</version> 5 </dependency>
附完整pom.xml
1 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 2 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> 3 <parent> 4 <artifactId>shiro-example</artifactId> 5 <groupId>com.github.zhangkaitao</groupId> 6 <version>1.0-SNAPSHOT</version> 7 </parent> 8 <modelVersion>4.0.0</modelVersion> 9 <artifactId>shiro-example-chapter17-client</artifactId> 10 <packaging>war</packaging> 11 <name>shiro-example-chapter17-client</name> 12 <url>http://maven.apache.org</url> 13 <dependencies> 14 <dependency> 15 <groupId>junit</groupId> 16 <artifactId>junit</artifactId> 17 <version>3.8.1</version> 18 <scope>test</scope> 19 </dependency> 20 21 <dependency> 22 <groupId>commons-collections</groupId> 23 <artifactId>commons-collections</artifactId> 24 <version>3.2.1</version> 25 </dependency> 26 27 28 <dependency> 29 <groupId>org.apache.oltu.oauth2</groupId> 30 <artifactId>org.apache.oltu.oauth2.client</artifactId> 31 <version>0.31</version> 32 </dependency> 33 34 35 <dependency> 36 <groupId>javax.servlet</groupId> 37 <artifactId>javax.servlet-api</artifactId> 38 <version>3.0.1</version> 39 <scope>provided</scope> 40 </dependency> 41 <dependency> 42 <groupId>javax.servlet.jsp</groupId> 43 <artifactId>jsp-api</artifactId> 44 <version>2.2</version> 45 </dependency> 46 <dependency> 47 <groupId>javax.servlet</groupId> 48 <artifactId>jstl</artifactId> 49 <version>1.2</version> 50 </dependency> 51 52 53 <dependency> 54 <groupId>org.apache.shiro</groupId> 55 <artifactId>shiro-core</artifactId> 56 <version>1.2.2</version> 57 </dependency> 58 <dependency> 59 <groupId>org.apache.shiro</groupId> 60 <artifactId>shiro-ehcache</artifactId> 61 <version>1.2.2</version> 62 </dependency> 63 <dependency> 64 <groupId>org.apache.shiro</groupId> 65 <artifactId>shiro-web</artifactId> 66 <version>1.2.2</version> 67 </dependency> 68 <dependency> 69 <groupId>org.apache.shiro</groupId> 70 <artifactId>shiro-quartz</artifactId> 71 <version>1.2.2</version> 72 </dependency> 73 <dependency> 74 <groupId>org.apache.shiro</groupId> 75 <artifactId>shiro-spring</artifactId> 76 <version>1.2.2</version> 77 </dependency> 78 79 80 <dependency> 81 <groupId>mysql</groupId> 82 <artifactId>mysql-connector-java</artifactId> 83 <version>5.1.25</version> 84 </dependency> 85 <dependency> 86 <groupId>com.alibaba</groupId> 87 <artifactId>druid</artifactId> 88 <version>0.2.23</version> 89 </dependency> 90 91 92 <!-- aspectj相关jar包--> 93 <dependency> 94 <groupId>org.aspectj</groupId> 95 <artifactId>aspectjrt</artifactId> 96 <version>1.7.4</version> 97 </dependency> 98 <dependency> 99 <groupId>org.aspectj</groupId> 100 <artifactId>aspectjweaver</artifactId> 101 <version>1.7.4</version> 102 </dependency> 103 104 <dependency> 105 <groupId>org.springframework</groupId> 106 <artifactId>spring-context-support</artifactId> 107 <version>4.0.0.RELEASE</version> 108 </dependency> 109 110 <dependency> 111 <groupId>org.springframework</groupId> 112 <artifactId>spring-jdbc</artifactId> 113 <version>4.0.0.RELEASE</version> 114 </dependency> 115 116 <dependency> 117 <groupId>org.springframework</groupId> 118 <artifactId>spring-tx</artifactId> 119 <version>4.0.0.RELEASE</version> 120 </dependency> 121 122 <dependency> 123 <groupId>org.springframework</groupId> 124 <artifactId>spring-webmvc</artifactId> 125 <version>4.0.0.RELEASE</version> 126 </dependency> 127 128 <!--jackson --> 129 <dependency> 130 <groupId>com.fasterxml.jackson.core</groupId> 131 <artifactId>jackson-databind</artifactId> 132 <version>2.2.3</version> 133 </dependency> 134 135 </dependencies> 136 <build> 137 <finalName>chapter17-client</finalName> 138 <plugins> 139 <plugin> 140 <groupId>org.mortbay.jetty</groupId> 141 <artifactId>jetty-maven-plugin</artifactId> 142 <version>8.1.8.v20121106</version> 143 <configuration> 144 <webAppConfig> 145 <contextPath>/${project.build.finalName}</contextPath> 146 </webAppConfig> 147 <connectors> 148 <connector implementation="org.eclipse.jetty.server.nio.SelectChannelConnector"> 149 <port>9080</port> 150 <maxIdleTime>60000</maxIdleTime> 151 </connector> 152 </connectors> 153 </configuration> 154 </plugin> 155 156 157 <plugin> 158 <groupId>org.apache.tomcat.maven</groupId> 159 <artifactId>tomcat7-maven-plugin</artifactId> 160 <version>2.2</version> 161 <configuration> 162 <path>/${project.build.finalName}</path> 163 <httpPort>9080</httpPort> 164 </configuration> 165 166 </plugin> 167 </plugins> 168 169 170 </build> 171 </project>
(2)代码
代码总览:
OAuth2Token:类似于UsernamePasswordToken,用于存储oauth2服务端返回的auth code。
1 import org.apache.shiro.authc.AuthenticationToken; 2 3 public class OAuth2Token implements AuthenticationToken { 4 5 public OAuth2Token(String authCode) { 6 this.authCode = authCode; 7 } 8 9 private String authCode; 10 private String principal; 11 12 //getter、setter略。 13 14 @Override 15 public Object getCredentials() { 16 return authCode; 17 } 18 }
OAuth2AuthenticationFilter :类似于前面提过的FormAuthenticationFilter,用于OAuth客户端的身份验证控制。
整个的操作流程,和前面的shiro登录其实是很类似的。
1 如果当前用户还没有身份验证,首先判断url中是否有auth code,如果没有则重定向到服务端进行登录授权,得到auth code。 2 有了auth code后,OAuth2AuthenticationFilter 用auth code创建Auth2Token。 3 Subject.login利用Auth2Token进行登录。 4 OAuth2Realm 根据OAuth2Token进行相应的登录逻辑处理。
1 package com.github.zhangkaitao.shiro.chapter18.oauth2; 2 3 import org.apache.shiro.authc.AuthenticationException; 4 import org.apache.shiro.authc.AuthenticationToken; 5 import org.apache.shiro.subject.Subject; 6 import org.apache.shiro.web.filter.AccessControlFilter; 7 import org.apache.shiro.web.filter.authc.AuthenticatingFilter; 8 import org.apache.shiro.web.filter.authc.AuthenticationFilter; 9 import org.apache.shiro.web.util.WebUtils; 10 import org.springframework.util.StringUtils; 11 12 import javax.servlet.ServletRequest; 13 import javax.servlet.ServletResponse; 14 import javax.servlet.http.HttpServletRequest; 15 import java.io.IOException; 16 17 public class OAuth2AuthenticationFilter extends AuthenticatingFilter { 18 20 private String authcCodeParam = "code";//oauth2 authc code参数名 22 private String clientId; //客户端id 24 private String redirectUrl; //服务器端登录成功/失败后重定向到的客户端地址 26 private String responseType = "code"; //oauth2服务器响应类型 28 private String failureUrl; 29 30 //setter略。没有getter。 31 32 @Override 33 protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) throws Exception { 34 HttpServletRequest httpRequest = (HttpServletRequest) request; 35 String code = httpRequest.getParameter(authcCodeParam);//从url中拿到auth code 36 return new OAuth2Token(code);//用auth code创建auth2Token 37 } 38 39 @Override 40 protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) { 41 return false; 42 } 43 44 @Override 45 protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception { 48 String error = request.getParameter("error");//首先判定有没有error,有的话直接重定向到失败页面 49 String errorDescription = request.getParameter("error_description"); 50 if(!StringUtils.isEmpty(error)) {//如果服务端返回了错误 51 WebUtils.issueRedirect(request, response, failureUrl + "?error=" + error + "error_description=" + errorDescription); 52 return false; 53 } 54 55 Subject subject = getSubject(request, response); 56 if(!subject.isAuthenticated()) { 57 if(StringUtils.isEmpty(request.getParameter(authcCodeParam))) { 58 //如果用户没有身份验证,且没有auth code,则重定向到服务端授权 59 saveRequestAndRedirectToLogin(request, response); 60 return false; 61 } 62 } 64 return executeLogin(request, response);//执行父类的登录逻辑,调用Subject.login登录 65 } 66 67 @Override//登录成功后的回调方法 68 protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, 69 ServletResponse response) throws Exception { 70 issueSuccessRedirect(request, response); 71 return false; 72 } 73 74 @Override//登录失败后的回调方法 75 protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException ae, ServletRequest request, 76 ServletResponse response) { 77 Subject subject = getSubject(request, response); 78 if (subject.isAuthenticated() || subject.isRemembered()) { 79 try { 80 issueSuccessRedirect(request, response); 81 } catch (Exception e) { 82 e.printStackTrace(); 83 } 84 } else { 85 try { 86 WebUtils.issueRedirect(request, response, failureUrl); 87 } catch (IOException e) { 88 e.printStackTrace(); 89 } 90 } 91 return false; 92 } 93 94 }
OAuth2Realm:
1 OAuth2Realm 首先只支持 OAuth2Token 类型的 Token 2 通过传入的 auth code 去换取 access token 3 再根据 access token 去获取用户信息(用户名) 4 然后根据此信息创建AuthenticationInfo 5 如果需要 AuthorizationInfo 信息,可以根据此处获取的用户名再根据自己的业务规则去获取。
1 package com.github.zhangkaitao.shiro.chapter18.oauth2; 2 3 import org.apache.oltu.oauth2.client.OAuthClient; 4 import org.apache.oltu.oauth2.client.URLConnectionClient; 5 import org.apache.oltu.oauth2.client.request.OAuthBearerClientRequest; 6 import org.apache.oltu.oauth2.client.request.OAuthClientRequest; 7 import org.apache.oltu.oauth2.client.response.OAuthAccessTokenResponse; 8 import org.apache.oltu.oauth2.client.response.OAuthJSONAccessTokenResponse; 9 import org.apache.oltu.oauth2.client.response.OAuthResourceResponse; 10 import org.apache.oltu.oauth2.common.OAuth; 11 import org.apache.oltu.oauth2.common.message.types.GrantType; 12 import org.apache.shiro.authc.*; 13 import org.apache.shiro.authz.AuthorizationInfo; 14 import org.apache.shiro.authz.SimpleAuthorizationInfo; 15 import org.apache.shiro.realm.AuthorizingRealm; 16 import org.apache.shiro.subject.PrincipalCollection; 17 18 public class OAuth2Realm extends AuthorizingRealm { 19 20 private String clientId; 21 private String clientSecret; 22 private String accessTokenUrl; 23 private String userInfoUrl; 24 private String redirectUrl; 25 26 //省略setter。 27 28 @Override 29 public boolean supports(AuthenticationToken token) { 30 return token instanceof OAuth2Token;//表示此Realm只支持OAuth2Token类型 31 } 32 33 @Override 34 protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {//授权 35 SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo(); 36 return authorizationInfo; 37 } 38 39 @Override 40 protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {//验证 41 OAuth2Token oAuth2Token = (OAuth2Token) token; 42 String code = oAuth2Token.getAuthCode(); 43 String username = extractUsername(code); 44 45 SimpleAuthenticationInfo authenticationInfo = 46 new SimpleAuthenticationInfo(username, code, getName()); 47 return authenticationInfo; 48 } 49 50 private String extractUsername(String code) { 51 52 try { 53 OAuthClient oAuthClient = new OAuthClient(new URLConnectionClient()); 54 55 OAuthClientRequest accessTokenRequest = OAuthClientRequest 56 .tokenLocation(accessTokenUrl) 57 .setGrantType(GrantType.AUTHORIZATION_CODE) 58 .setClientId(clientId) 59 .setClientSecret(clientSecret) 60 .setCode(code) 61 .setRedirectURI(redirectUrl) 62 .buildQueryMessage(); 63 64 OAuthAccessTokenResponse oAuthResponse = oAuthClient.accessToken(accessTokenRequest, OAuth.HttpMethod.POST); 65 66 String accessToken = oAuthResponse.getAccessToken(); 67 Long expiresIn = oAuthResponse.getExpiresIn(); 68 69 OAuthClientRequest userInfoRequest = new OAuthBearerClientRequest(userInfoUrl) 70 .setAccessToken(accessToken).buildQueryMessage(); 71 72 OAuthResourceResponse resourceResponse = oAuthClient.resource(userInfoRequest, OAuth.HttpMethod.GET, OAuthResourceResponse.class); 73 String username = resourceResponse.getBody(); 74 return username; 75 } catch (Exception e) { 76 e.printStackTrace(); 77 throw new OAuth2AuthenticationException(e); 78 } 79 } 80 }
(3)配置文件spring-config-shiro.xml
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:util="http://www.springframework.org/schema/util" 4 xmlns:aop="http://www.springframework.org/schema/aop" 5 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 6 xsi:schemaLocation=" 7 http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd 8 http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd 9 http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> 10 11 <!-- 缓存管理器 --> 12 <bean id="cacheManager" class="org.apache.shiro.cache.ehcache.EhCacheManager"> 13 <property name="cacheManagerConfigFile" value="classpath:ehcache/ehcache.xml"/> 14 </bean> 15 16 <!-- Realm实现 --> 17 <bean id="oAuth2Realm" class="com.github.zhangkaitao.shiro.chapter18.oauth2.OAuth2Realm"> 18 <property name="cachingEnabled" value="true"/> 19 <property name="authenticationCachingEnabled" value="true"/> 20 <property name="authenticationCacheName" value="authenticationCache"/> 21 <property name="authorizationCachingEnabled" value="true"/> 22 <property name="authorizationCacheName" value="authorizationCache"/> 23 24 <property name="clientId" value="c1ebe466-1cdc-4bd3-ab69-77c3561b9dee"/> 25 <property name="clientSecret" value="d8346ea2-6017-43ed-ad68-19c0f971738b"/> 26 <property name="accessTokenUrl" value="http://localhost:8080/chapter17-server/accessToken"/> 27 <property name="userInfoUrl" value="http://localhost:8080/chapter17-server/userInfo"/> 28 <property name="redirectUrl" value="http://localhost:9080/chapter17-client/oauth2-login"/> 29 </bean> 30 31 <!-- 会话ID生成器 --> 32 <bean id="sessionIdGenerator" class="org.apache.shiro.session.mgt.eis.JavaUuidSessionIdGenerator"/> 33 34 <!-- 会话Cookie模板 --> 35 <bean id="sessionIdCookie" class="org.apache.shiro.web.servlet.SimpleCookie"> 36 <constructor-arg value="sid"/> 37 <property name="httpOnly" value="true"/> 38 <property name="maxAge" value="-1"/> 39 </bean> 40 41 <bean id="rememberMeCookie" class="org.apache.shiro.web.servlet.SimpleCookie"> 42 <constructor-arg value="rememberMe"/> 43 <property name="httpOnly" value="true"/> 44 <property name="maxAge" value="2592000"/><!-- 30天 --> 45 </bean> 46 47 <!-- rememberMe管理器 --> 48 <bean id="rememberMeManager" class="org.apache.shiro.web.mgt.CookieRememberMeManager"> 49 <!-- rememberMe cookie加密的密钥 建议每个项目都不一样 默认AES算法 密钥长度(128 256 512 位)--> 50 <property name="cipherKey" 51 value="#{T(org.apache.shiro.codec.Base64).decode('4AvVhmFLUs0KTA3Kprsdag==')}"/> 52 <property name="cookie" ref="rememberMeCookie"/> 53 </bean> 54 55 <!-- 会话DAO --> 56 <bean id="sessionDAO" class="org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO"> 57 <property name="activeSessionsCacheName" value="shiro-activeSessionCache"/> 58 <property name="sessionIdGenerator" ref="sessionIdGenerator"/> 59 </bean> 60 61 <!-- 会话验证调度器 --> 62 <bean id="sessionValidationScheduler" class="org.apache.shiro.session.mgt.quartz.QuartzSessionValidationScheduler"> 63 <property name="sessionValidationInterval" value="1800000"/> 64 <property name="sessionManager" ref="sessionManager"/> 65 </bean> 66 67 <!-- 会话管理器 --> 68 <bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager"> 69 <property name="globalSessionTimeout" value="1800000"/> 70 <property name="deleteInvalidSessions" value="true"/> 71 <property name="sessionValidationSchedulerEnabled" value="true"/> 72 <property name="sessionValidationScheduler" ref="sessionValidationScheduler"/> 73 <property name="sessionDAO" ref="sessionDAO"/> 74 <property name="sessionIdCookieEnabled" value="true"/> 75 <property name="sessionIdCookie" ref="sessionIdCookie"/> 76 </bean> 77 78 <!-- 安全管理器 --> 79 <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> 80 <property name="realm" ref="oAuth2Realm"/> 81 <property name="sessionManager" ref="sessionManager"/> 82 <property name="cacheManager" ref="cacheManager"/> 83 <property name="rememberMeManager" ref="rememberMeManager"/> 84 </bean> 85 86 <!-- 相当于调用SecurityUtils.setSecurityManager(securityManager) --> 87 <bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean"> 88 <property name="staticMethod" value="org.apache.shiro.SecurityUtils.setSecurityManager"/> 89 <property name="arguments" ref="securityManager"/> 90 </bean> 91 92 <!-- OAuth2身份验证过滤器 --> 93 <bean id="oAuth2AuthenticationFilter" class="com.github.zhangkaitao.shiro.chapter18.oauth2.OAuth2AuthenticationFilter"> 94 <property name="authcCodeParam" value="code"/> 95 <property name="failureUrl" value="/oauth2Failure.jsp"/> 96 </bean> 97 98 <!-- Shiro的Web过滤器 --> 99 <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> 100 <property name="securityManager" ref="securityManager"/> 101 <property name="loginUrl" value="http://localhost:8080/chapter17-server/authorize?client_id=c1ebe466-1cdc-4bd3-ab69-77c3561b9dee&response_type=code&redirect_uri=http://localhost:9080/chapter17-client/oauth2-login"/> 102 <property name="successUrl" value="/"/> 103 <property name="filters"> 104 <util:map> 105 <entry key="oauth2Authc" value-ref="oAuth2AuthenticationFilter"/> 106 </util:map> 107 </property> 108 <property name="filterChainDefinitions"> 109 <value> 110 / = anon 111 /oauth2Failure.jsp = anon 112 /oauth2-login = oauth2Authc 113 /logout = logout 114 /** = user 115 </value> 116 </property> 117 </bean> 118 119 <!-- Shiro生命周期处理器--> 120 <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/> 121 122 </beans>
这里的OAuth2Realm 需要配置在服务端申请的 clientId 和 clientSecret;
1 <!-- Realm实现 --> 2 <bean id="oAuth2Realm" class="com.github.zhangkaitao.shiro.chapter18.oauth2.OAuth2Realm"> 3 <property name="cachingEnabled" value="true"/> 4 <property name="authenticationCachingEnabled" value="true"/> 5 <property name="authenticationCacheName" value="authenticationCache"/> 6 <property name="authorizationCachingEnabled" value="true"/> 7 <property name="authorizationCacheName" value="authorizationCache"/> 8 9 <property name="clientId" value="c1ebe466-1cdc-4bd3-ab69-77c3561b9dee"/> 10 <property name="clientSecret" value="d8346ea2-6017-43ed-ad68-19c0f971738b"/> 11 <property name="accessTokenUrl" value="http://localhost:8080/chapter17-server/accessToken"/> 12 <property name="userInfoUrl" value="http://localhost:8080/chapter17-server/userInfo"/> 13 <property name="redirectUrl" value="http://localhost:9080/chapter17-client/oauth2-login"/> 14 </bean>
对应的数据参看server里的shiro-data.sql
然后配置刚刚的OAuth2AuthenticationFilter:
1 <!-- OAuth2身份验证过滤器 --> 2 <bean id="oAuth2AuthenticationFilter" class="com.github.zhangkaitao.shiro.chapter18.oauth2.OAuth2AuthenticationFilter"> 3 <property name="authcCodeParam" value="code"/> 4 <property name="failureUrl" value="/oauth2Failure.jsp"/> 5 </bean>
这个OAuth2AuthenticationFilter用来拦截服务端重定向回来的auth code。
这里的/oauth2-login = oauth2Authc 表示/oauth2-login 地址使用 oauth2Authc 拦截器拦截并进行 oauth2 客户端授权。
1 <!-- Shiro的Web过滤器 --> 2 <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> 3 <property name="securityManager" ref="securityManager"/> 4 <property name="loginUrl" value="http://localhost:8080/chapter17-server/authorize?client_id=c1ebe466-1cdc-4bd3-ab69-77c3561b9dee&response_type=code&redirect_uri=http://localhost:9080/chapter17-client/oauth2-login"/> 5 <property name="successUrl" value="/"/> 6 <property name="filters"> 7 <util:map> 8 <entry key="oauth2Authc" value-ref="oAuth2AuthenticationFilter"/> 9 </util:map> 10 </property> 11 <property name="filterChainDefinitions"> 12 <value> 13 / = anon 14 /oauth2Failure.jsp = anon 15 /oauth2-login = oauth2Authc 16 /logout = logout 17 /** = user 18 </value> 19 </property> 20 </bean>
(4)测试
a) 访问地址:http://localhost:9080/chapter17-client/ ,点击登录按钮,跳出如下界面:
b) 输入用户名,密码,并点击登录并授权。
c) 如果登录成功,服务端会重定向到客户端,根据配置文件里提供的地址为:http://localhost:9080/chapter17-client/oauth2-login?code=473d56015bcf576f2ca03eac1a5bcc11 。注意这个url是带着auth code的。
d) 客户端的OAuth2AuthenticationFilter拿到此auth code,用来创建OAuth2Token,提交给Subject用于登录。
e) 客户端的Subject委托给OAuth2Realm进行身份验证。
f) OAuth2Realm根据token获取受保护的用户信息,用于客户端登录。
至此,OAuth的集成就完成了,只完成了基本功能,没有进行一些关于异常的检测。具体的可以参考新浪微博进行API及异常错误码的设计。