一、源码代码介绍
Spring cloud oauth2.0的代码结构图如下:
可以看到Spring oauth2.0的代码结构分为了五层,client层负责应用客户端的管理。common层为公共类的所在地。config层为配置层。http.converter层为转换层。provider层是最重要的一层,它负责管理认证服务器和资源服务器。下面大致介绍每一层,欢迎一起讨论:
1.client层:如图
这里只讲其中的几个比较典型的类:
(1)OAuth2ClientAuthenticationProcessingFilter:负责对客户端的访问进行过滤,验证token处理等操作。
(2)JdbcClientTokenServices:里面封装了对oauth_client_token表的一系列操作(如增删改查),此表保存的是此client的token信息。
(3)OAuth2RestTemplate:实现了OAuthRestOperations接口,主要是用来去调用受保护资源,会自己带上当前的token信息。
当然还有其它的一些类,比如DefaultOAuth2ClientContext实现了OAuth2ClientContext接口,里面存储了client的上下文信息。
2.common公共层:如图*
可见这一层主要是对json字符串处理、解析和实例化等操作,
3.config层:
注解的定义,以及认证服务器和资源服务器的各个配置类的定义都在这个包,是贯穿整个代码非常核心的一层。如图:
可见,我们在代码里所用的三个基本注解EnableAuthorizationServer、EnableOAuth2Client、EnableResourceServer都在这里。还有
AuthorizationServerConfigurerAdapter、ResourceServerConfigurerAdapter这两个最核心的配置类也在这里,只要实现这两个核心配置类,复写他们的函数便可以实现
个性化的配置。
**(4)converter层:
(5)provider层:**
最核心的一层,里面包含了endpoint、认证、以及四种授权方式的管理。如图:
(1)关于approval包:里面包含了授权的信息,如果想要自定义展示页面上的元素,需要研读此包里面的代码进行改写。
(2)authentication包:这一层只讲一个类,就是OAuth2AuthenticationProcessingFilter类,他主要负责当资源服务器接收到请求,此类将进行过滤校验等操作。
(3)client包:这里只讲一个JdbcClientDetailsService一个类,打开此类的源码可以看到它里面声明了很多的sql语句。此类主要是关系着client信息的管理。以jdbc的方式进行读取。
(4)code
、implicit
、password
、refresh
层这里就不再展开,就是对应着四种授权方式的管理。
(5)endpoint层:此层很重要,代码里面定义了基本的接口地址,如/oauth/authorize
、/oauth/token
、/oauth/confirm_access
等等。这里只说一下WhitelabelApprovalEndpoint类,此类里面声明了@RequestMapping({"/oauth/confirm_access"})这个endpoint,这个endpoint就是生成我们的授权页面,就是那个有很多选项,让你选择允许还是拒绝的那个页面,这里此类并没有对应的web页面,而是在java代码里创建了一个html的ModelAndView视图,然后展示出来。
具体生成自定义授权页面方式如下:
@Controller
@SessionAttributes({"authorizationRequest"})
public class OAuthController {
@RequestMapping({ "/oauth/my_approval"})
@ResponseBody
public JSONObject getAccessConfirmation(Map<String, Object> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
@SuppressWarnings("unchecked")
Map<String, String> scopes = (Map<String, String>) (model.containsKey("scopes") ? model.get("scopes") : request.getAttribute("scopes"));
List<String> scopeList = new ArrayList<>();
for (String scope : scopes.keySet()) {
scopeList.add(scope);
}
JSONObject jsonObject = new JSONObject();
jsonObject.put("scopeList",scopeList);
return jsonObject;
}
@RequestMapping({ "/oauth/my_error" })
public String handleError(Map<String, Object> model, HttpServletRequest request) {
Object error = request.getAttribute("error");
String errorSummary;
if (error instanceof OAuth2Exception) {
OAuth2Exception oauthError = (OAuth2Exception) error;
errorSummary = HtmlUtils.htmlEscape(oauthError.getSummary());
} else {
errorSummary = "Unknown error";
}
model.put("errorSummary", errorSummary);
return "oauth_error";
}
}
@SessionAttributes({"authorizationRequest"})这个注解必须带上不清楚此注解用法的请自行百度
然后在授权类中自动注入authorizationEndpoint并参考源代码类org.springframework.security.oauth2.provider.endpoint.AuthorizationEndpoint将授权链接替换掉
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
private static String REALM="MY_OAUTH_REALM";
@Autowired
AuthenticationManager authenticationManager;
@Autowired
private TokenStore tokenStore;
@Autowired
AuthorizationEndpoint authorizationEndpoint;
@Autowired
CustomUserDetailsService customUserDetailsService;
@Autowired
private UserApprovalHandler userApprovalHandler;
@Autowired
private ClientDetailsService clientDetailsService;
@Bean
@Autowired
public TokenStoreUserApprovalHandler userApprovalHandler(TokenStore tokenStore){
TokenStoreUserApprovalHandler handler = new TokenStoreUserApprovalHandler();
handler.setTokenStore(tokenStore);
handler.setRequestFactory(new DefaultOAuth2RequestFactory(clientDetailsService));
handler.setClientDetailsService(clientDetailsService);
return handler;
}
@Bean
@Autowired
public ApprovalStore approvalStore(TokenStore tokenStore) throws Exception {
TokenApprovalStore store = new TokenApprovalStore();
store.setTokenStore(tokenStore);
return store;
}
@PostConstruct
public void init() {
authorizationEndpoint.setUserApprovalPage("forward:/oauth/my_approval");
authorizationEndpoint.setErrorPage("forward:/oauth/my_error");
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("client")
.authorizedGrantTypes("password", "authorization_code", "refresh_token", "implicit")
.authorities("ROLE_CLIENT", "ROLE_TRUSTED_CLIENT")
.scopes("read", "write", "trust")
.secret("secret")
.accessTokenValiditySeconds(120).//Access token is only valid for 2 minutes.
refreshTokenValiditySeconds(600);//Refresh token is only valid for 10 minutes.
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints
.authenticationManager(authenticationManager)
.userDetailsService(customUserDetailsService)
.userApprovalHandler(userApprovalHandler)
.tokenEnhancer(new CustomTokenEnhancer())
.tokenStore(tokenStore);
//第二种方式自定义授权页如下,如果是这种方式 在自定义controller中不需要添加@SessionAttributes({"authorizationRequest"}),大家可以大胆尝试一下
//endpoints.pathMapping(String"/oauth/confirm_access","/oauth/my_approval");
}
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security
.allowFormAuthenticationForClients()
.tokenKeyAccess("permitAll()")
.checkTokenAccess("isAuthenticated()")
.realm(REALM+"/client");
}
}
以上是我参考各个网友博客和自己的总结,在使用中也遇见了很多的坑和错误,希望能和大家一起探讨探讨,由于工作中发生的错误不能一一列举出来,欢迎大家留言,我看见了一定尽力回复,大家想看一下在项目中的实践可以参考本人的github地址
参考本人github地址:https://github.com/dqqzj/spring4all/tree/master/oauth2