Java 实现oauth授权开发 java author
  rxxAR4JzfL2J 2023年12月22日 30 0


OAuth2 Java Shiro 客户端

http://jinnianshilongnian.iteye.com/blog/2038646


客户端

客户端流程:如果需要登录首先跳到oauth2服务端进行登录授权,成功后服务端返回auth code,然后客户端使用auth code去服务器端换取access token,最好根据access token获取用户信息进行客户端的登录绑定。这个可以参照如很多网站的新浪微博登录功能,或其他的第三方帐号登录功能。

POM依赖

此处我们使用apache oltu oauth2客户端实现。     


1. <dependency>  
2.   <groupId>org.apache.oltu.oauth2</groupId>  
3.   <artifactId>org.apache.oltu.oauth2.client</artifactId>  
4. 0.31</version>  
5. </dependency>


其他的请参考pom.xml。

 

OAuth2Token

类似于UsernamePasswordToken和CasToken;用于存储oauth2服务端返回的auth code。  

1. public class OAuth2Token implements
2. private
3. private
4. public
5. this.authCode = authCode;  
6.     }  
7. //省略getter/setter
8. }


  

OAuth2AuthenticationFilter

该filter的作用类似于FormAuthenticationFilter用于oauth2客户端的身份验证控制;如果当前用户还没有身份验证,首先会判断url中是否有code(服务端返回的auth code),如果没有则重定向到服务端进行登录并授权,然后返回auth code;接着OAuth2AuthenticationFilter会用auth code创建OAuth2Token,然后提交给Subject.login进行登录;接着OAuth2Realm会根据OAuth2Token进行相应的登录逻辑。  


1. public class OAuth2AuthenticationFilter extends
2. //oauth2 authc code参数名
3. private String authcCodeParam = "code";  
4. //客户端id
5. private
6. //服务器端登录成功/失败后重定向到的客户端地址
7. private
8. //oauth2服务器响应类型
9. private String responseType = "code";  
10. private
11. //省略setter
12. protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) throws
13.         HttpServletRequest httpRequest = (HttpServletRequest) request;  
14.         String code = httpRequest.getParameter(authcCodeParam);  
15. return new
16.     }  
17. protected boolean
18. return false;  
19.     }  
20. protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws
21. "error");  
22. "error_description");  
23. if(!StringUtils.isEmpty(error)) {//如果服务端返回了错误
24. "?error=" + error + "error_descriptinotallow="
25. return false;  
26.         }  
27.         Subject subject = getSubject(request, response);  
28. if(!subject.isAuthenticated()) {  
29. if(StringUtils.isEmpty(request.getParameter(authcCodeParam))) {  
30. //如果用户没有身份验证,且没有auth code,则重定向到服务端授权
31.                 saveRequestAndRedirectToLogin(request, response);  
32. return false;  
33.             }  
34.         }  
35. //执行父类里的登录逻辑,调用Subject.login登录
36. return
37.     }  
38.   
39. //登录成功后的回调方法 重定向到成功页面
40. protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request,  ServletResponse response) throws
41.         issueSuccessRedirect(request, response);  
42. return false;  
43.     }  
44.   
45. //登录失败后的回调 
46. protected boolean
47.                                      ServletResponse response) {  
48.         Subject subject = getSubject(request, response);  
49. if
50. try { //如果身份验证成功了 则也重定向到成功页面
51.                 issueSuccessRedirect(request, response);  
52. catch
53.                 e.printStackTrace();  
54.             }  
55. else
56. try { //登录失败时重定向到失败页面
57.                 WebUtils.issueRedirect(request, response, failureUrl);  
58. catch
59.                 e.printStackTrace();  
60.             }  
61.         }  
62. return false;  
63.     }  
64. }


该拦截器的作用:

1、首先判断有没有服务端返回的error参数,如果有则直接重定向到失败页面;

2、接着如果用户还没有身份验证,判断是否有auth code参数(即是不是服务端授权之后返回的),如果没有则重定向到服务端进行授权;

3、否则调用executeLogin进行登录,通过auth code创建OAuth2Token提交给Subject进行登录;

4、登录成功将回调onLoginSuccess方法重定向到成功页面;

5、登录失败则回调onLoginFailure重定向到失败页面。

 

OAuth2Realm  


1. public class OAuth2Realm extends
2. private
3. private
4. private
5. private
6. private
7. //省略setter
8. public boolean
9. return token instanceof OAuth2Token; //表示此Realm只支持OAuth2Token类型
10.     }  
11. protected
12. new
13. return
14.     }  
15. protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws
16.         OAuth2Token oAuth2Token = (OAuth2Token) token;  
17. //获取 auth code
18. // 提取用户名
19.         SimpleAuthenticationInfo authenticationInfo =  
20. new
21. return
22.     }  
23. private
24. try
25. new OAuthClient(new
26.             OAuthClientRequest accessTokenRequest = OAuthClientRequest  
27.                     .tokenLocation(accessTokenUrl)  
28.                     .setGrantType(GrantType.AUTHORIZATION_CODE)  
29.                     .setClientId(clientId).setClientSecret(clientSecret)  
30.                     .setCode(code).setRedirectURI(redirectUrl)  
31.                     .buildQueryMessage();  
32. //获取access token
33.             OAuthAccessTokenResponse oAuthResponse =   
34.                 oAuthClient.accessToken(accessTokenRequest, OAuth.HttpMethod.POST);  
35.             String accessToken = oAuthResponse.getAccessToken();  
36.             Long expiresIn = oAuthResponse.getExpiresIn();  
37. //获取user info
38.             OAuthClientRequest userInfoRequest =   
39. new
40.                     .setAccessToken(accessToken).buildQueryMessage();  
41.             OAuthResourceResponse resourceResponse = oAuthClient.resource(  
42. class);  
43.             String username = resourceResponse.getBody();  
44. return
45. catch
46. throw new
47.         }  
48.     }  
49. }


此Realm首先只支持OAuth2Token类型的Token;然后通过传入的auth code去换取access token;再根据access token去获取用户信息(用户名),然后根据此信息创建AuthenticationInfo;如果需要AuthorizationInfo信息,可以根据此处获取的用户名再根据自己的业务规则去获取。

 

Spring shiro配置(spring-config-shiro.xml)  

1. <bean id="oAuth2Realm"
2. class="com.github.zhangkaitao.shiro.chapter18.oauth2.OAuth2Realm">  
3. "cachingEnabled" value="true"/>  
4. "authenticationCachingEnabled" value="true"/>  
5. "authenticationCacheName" value="authenticationCache"/>  
6. "authorizationCachingEnabled" value="true"/>  
7. "authorizationCacheName" value="authorizationCache"/>  
8. "clientId" value="c1ebe466-1cdc-4bd3-ab69-77c3561b9dee"/>  
9. "clientSecret" value="d8346ea2-6017-43ed-ad68-19c0f971738b"/>  
10. "accessTokenUrl"
11. "http://localhost:8080/chapter17-server/accessToken"/>  
12. "userInfoUrl" value="http://localhost:8080/chapter17-server/userInfo"/>  
13. "redirectUrl" value="http://localhost:9080/chapter17-client/oauth2-login"/>  
14. </bean>


此OAuth2Realm需要配置在服务端申请的clientId和clientSecret;及用于根据auth code换取access token的accessTokenUrl地址;及用于根据access token换取用户信息(受保护资源)的userInfoUrl地址。 

 



1. <bean id="oAuth2AuthenticationFilter"
2. class="com.github.zhangkaitao.shiro.chapter18.oauth2.OAuth2AuthenticationFilter">  
3. "authcCodeParam" value="code"/>  
4. "failureUrl" value="/oauth2Failure.jsp"/>  
5. </bean>



此OAuth2AuthenticationFilter用于拦截服务端重定向回来的auth code。  

 



1. <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">  
2. "securityManager" ref="securityManager"/>  
3. "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"/>  
4. "successUrl" value="/"/>  
5. "filters">  
6.       <util:map>  
7. "oauth2Authc" value-ref="oAuth2AuthenticationFilter"/>  
8.       </util:map>  
9.   </property>  
10. "filterChainDefinitions">  
11.       <value>  
12.           / = anon  
13.           /oauth2Failure.jsp = anon  
14.           /oauth2-login = oauth2Authc  
15.           /logout = logout  
16.           /** = user  
17.       </value>  
18.   </property>  
19. </bean>


此处设置loginUrl为http://localhost:8080/chapter17-server/authorize

?client_id=c1ebe466-1cdc-4bd3-ab69-77c3561b9dee&amp;response_type=code&amp;redirect_uri=http://localhost:9080/chapter17-client/oauth2-login";其会自动设置到所有的AccessControlFilter,如oAuth2AuthenticationFilter;另外/oauth2-login = oauth2Authc表示/oauth2-login地址使用oauth2Authc拦截器拦截并进行oauth2客户端授权。

 

测试

1、首先访问http://localhost:9080/chapter17-client/,然后点击登录按钮进行登录,会跳到如下页面: 

Java 实现oauth授权开发 java author_重定向

 

2、输入用户名进行登录并授权;

3、如果登录成功,服务端会重定向到客户端,即之前客户端提供的地址http://localhost:9080/chapter17-client/oauth2-login?code=473d56015bcf576f2ca03eac1a5bcc11,并带着auth code过去;

4、客户端的OAuth2AuthenticationFilter会收集此auth code,并创建OAuth2Token提交给Subject进行客户端登录;

5、客户端的Subject会委托给OAuth2Realm进行身份验证;此时OAuth2Realm会根据auth code换取access token,再根据access token获取受保护的用户信息;然后进行客户端登录。

 

到此OAuth2的集成就完成了,此处的服务端和客户端相对比较简单,没有进行一些异常检测,请参考如新浪微博进行相应API及异常错误码的设计。   

    

 

 

示例源代码:https://github.com/zhangkaitao/shiro-example



【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年12月22日 0

暂无评论

推荐阅读
rxxAR4JzfL2J