Spring Security Oauth2系列(二)

时间:2022-12-19 21:29:33

一、源码代码介绍

Spring cloud oauth2.0的代码结构图如下:

Spring Security Oauth2系列(二)可以看到Spring oauth2.0的代码结构分为了五层,client层负责应用客户端的管理。common层为公共类的所在地。config层为配置层。http.converter层为转换层。provider层是最重要的一层,它负责管理认证服务器和资源服务器。下面大致介绍每一层,欢迎一起讨论:

1.client层:如图

Spring Security Oauth2系列(二)这里只讲其中的几个比较典型的类:

(1)OAuth2ClientAuthenticationProcessingFilter:负责对客户端的访问进行过滤,验证token处理等操作。

(2)JdbcClientTokenServices:里面封装了对oauth_client_token表的一系列操作(如增删改查),此表保存的是此client的token信息。

(3)OAuth2RestTemplate:实现了OAuthRestOperations接口,主要是用来去调用受保护资源,会自己带上当前的token信息。

当然还有其它的一些类,比如DefaultOAuth2ClientContext实现了OAuth2ClientContext接口,里面存储了client的上下文信息。

2.common公共层:如图*

Spring Security Oauth2系列(二)可见这一层主要是对json字符串处理、解析和实例化等操作,

3.config层:

注解的定义,以及认证服务器和资源服务器的各个配置类的定义都在这个包,是贯穿整个代码非常核心的一层。如图:

Spring Security Oauth2系列(二)可见,我们在代码里所用的三个基本注解EnableAuthorizationServer、EnableOAuth2Client、EnableResourceServer都在这里。还有

AuthorizationServerConfigurerAdapter、ResourceServerConfigurerAdapter这两个最核心的配置类也在这里,只要实现这两个核心配置类,复写他们的函数便可以实现
个性化的配置。

**(4)converter层:

Spring Security Oauth2系列(二)

(5)provider层:**

最核心的一层,里面包含了endpoint、认证、以及四种授权方式的管理。如图:
Spring Security Oauth2系列(二)

(1)关于approval包:里面包含了授权的信息,如果想要自定义展示页面上的元素,需要研读此包里面的代码进行改写。

(2)authentication包:这一层只讲一个类,就是OAuth2AuthenticationProcessingFilter类,他主要负责当资源服务器接收到请求,此类将进行过滤校验等操作。

(3)client包:这里只讲一个JdbcClientDetailsService一个类,打开此类的源码可以看到它里面声明了很多的sql语句。此类主要是关系着client信息的管理。以jdbc的方式进行读取。
(4)codeimplicitpasswordrefresh层这里就不再展开,就是对应着四种授权方式的管理。

(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



此文转载地址  http://www.spring4all.com/article/885