美文网首页
Spring cloud oauth2 研究--授权方式分析

Spring cloud oauth2 研究--授权方式分析

作者: 输入昵称就行 | 来源:发表于2019-05-11 23:51 被阅读0次

密码模式授权相对来说比较简单,只使用到了申请token的接口也就是/oauth/token的接口,授权码模式相对复杂一点,以下为介绍:

1.增加AuthorizationServer配置

为了让数据更直观,将授权码模式的一些存储方式改为jdbc的

/// ### AuthorizationServerConfiguration.java

    @Bean
    public AuthorizationCodeServices authorizationCodeServices() {
    // 授权码模式code存储方式,默认内存存储
        return new JdbcAuthorizationCodeServices(dataSource);
    }

    
    @Bean
    public ApprovalStore approvalStore() {
    // 授权允许存储方式,默认内存存储
        return new JdbcApprovalStore(dataSource);
    }
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints
                // 授权允许存储方式
                .approvalStore(approvalStore())
                // 授权码模式code存储方式
                .authorizationCodeServices(authorizationCodeServices())
                // token存储方式
                .tokenStore(tokenStore())
                // 使用jwt增强
                .tokenEnhancer(jwtAccessTokenConverter())
                .allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST)
                .authenticationManager(authenticationManager);

        // 配置tokenServices参数
        DefaultTokenServices tokenServices = new DefaultTokenServices();
        tokenServices.setTokenStore(endpoints.getTokenStore());
        tokenServices.setSupportRefreshToken(true);
        tokenServices.setClientDetailsService(endpoints.getClientDetailsService());
        tokenServices.setTokenEnhancer(endpoints.getTokenEnhancer());
        endpoints.tokenServices(tokenServices);
    }

2.增加webSecurity的配置,由于需要访问服务下的登陆与允许授权的页面,需要配置permitall

@Configuration
@EnableWebSecurity
@Order(10)
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Bean
    public PasswordEncoder passwordEncoder() {
        return PasswordEncoderFactories.createDelegatingPasswordEncoder();
    }

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Autowired
    private UserServiceDetail userServiceDetail;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth
                .parentAuthenticationManager(authenticationManagerBean())
                .userDetailsService(userServiceDetail)
                .passwordEncoder(passwordEncoder());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http    
        // 登陆位置会有rememberMe选项
                .rememberMe()
                .and()
                .authorizeRequests()
         // 允许/login访问地址
                .anyRequest().authenticated()        
                .and()
                .formLogin().permitAll()
                .and()
                .logout()
                .logoutSuccessUrl("/login")
                .invalidateHttpSession(true)
                .permitAll()
                .and()
                .csrf().disable();
    }
}

3.数据库oauth_client_details增加一条记录

image.png

密码是123456的bcrypt方式加密

4.开始测试

  • 浏览器输入地址

http://localhost:4001/oauth/authorize?response_type=code&client_id=code&redirect_uri=http://baidu.com&state=123

response_type=code: 返回的是授权码
client_id=code: 与数据库的clientId对应
redirect_uri=http:/baidu.com: 与数据库web_redirect_uri对应,授权码code获取成功重定向的地址
state=123 任意的字符串

  • 跳转到login页面


    image.png

输入账号密码账号,重定向到redirect_uri,并且携带code: https://www.baidu.com/?code=vsf7NQ&state=123

  • 查看数据库

oauth_approvals 增加一条数据
oauth_code 增加一条数据,code=vsf7NQ

  • 通过得到的code,访问/oauth/token进行申请token,由于之前设置了允许方法GET,POST所以这两种请求方法都可以申请到token

http://localhost:4001/oauth/token?client_id=code&grant_type=authorization_code&redirect_uri=http://baidu.com&code=vsf7NQ&client_secret=123456

{
    "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NTcyOTk0NjEsInVzZXJfbmFtZSI6ImFkbWluIiwiYXV0aG9yaXRpZXMiOlsiYWRtaW4iXSwianRpIjoiMDUyNDdiMGItZjJiZS00MWZhLTlmODItOWRhZjU2NDZjNWI0IiwiY2xpZW50X2lkIjoiY29kZSIsInNjb3BlIjpbImFsbCJdfQ.-KDyRZyp-dWlFOk7XVnRg4EpDKzabspkzlKcr0M3tu4",
    "token_type": "bearer",
    "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX25hbWUiOiJhZG1pbiIsInNjb3BlIjpbImFsbCJdLCJhdGkiOiIwNTI0N2IwYi1mMmJlLTQxZmEtOWY4Mi05ZGFmNTY0NmM1YjQiLCJleHAiOjE1NTczMDMwNjEsImF1dGhvcml0aWVzIjpbImFkbWluIl0sImp0aSI6IjZhMWNiMmFkLWMxODEtNDcyZi1hYjIzLWY5ZjQ1MTQwMDJhMiIsImNsaWVudF9pZCI6ImNvZGUifQ.aG72owlXCaqdOTVw2NM5fALKMkWsW1Dce2zIvz_lqu4",
    "expires_in": 2248,
    "scope": "all",
    "jti": "05247b0b-f2be-41fa-9f82-9daf5646c5b4"
}
  • 再次查看oauth_code表,发现code=vsf7NQ的记录被删除了,说明授权码模式code只能被使用一次。

接下来通过源码来解析一下整个过程

拦截链

image.png
依次执行顺序为:
  1. WebAsyncManagerIntegrationFilter

    没做什么相关的操作

  2. SecurityContextPersistenceFilter

    创建安全上下文,请求结束时,清空,同时获取到用户,以及认证信息

  3. HeaderWriterFilter

    忽略

  4. LogoutFilter

    判断是否是登出操作;如果是,执行登出操作

  5. UsernamePasswordAuthenticationFilter

    判断哪些请求需要鉴权,哪些不需要,如果是登陆的接口,就会通过authenticationManager去进行校验,然后根据检验结果执行不同的操作

  6. DefaultLoginPageGeneratingFilter

    如果是登陆,登陆错误,登陆成功的请求,直接返回到页面

boolean loginError = isErrorPage(request);
boolean logoutSuccess = isLogoutSuccess(request);
if (isLoginUrlRequest(request) || loginError || logoutSuccess) {
    String loginPageHtml = generateLoginPageHtml(request, loginError,
                    logoutSuccess);
    response.setContentType("text/html;charset=UTF-8");
    response.setContentLength(loginPageHtml.getBytes(StandardCharsets.UTF_8).length);
    response.getWriter().write(loginPageHtml);

    return;
}
  1. RequestCacheAwareFilter

    判断使用缓存的请求

  2. SecurityContextHolderAwareRequestFilter

    忽略

  3. RememberMeAuthenticationFilter

    RememberMe的校验

public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
            throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) res;
        
        if (SecurityContextHolder.getContext().getAuthentication() == null) {
            Authentication rememberMeAuth = rememberMeServices.autoLogin(request,
                    response);
            if (rememberMeAuth != null) {
                // .... 从cookie里面获取登陆状态,成功直接结束请求,返回给用户
            }
            chain.doFilter(request, response);
        }
        // .....
    }

  1. AnonymousAuthenticationFilter

如果SecurityContextHolder中没有当前请求用户授权信息,创建一个匿名用户放到全局域SecurityContextHolder中

  1. SessionManagementFilter

判断session中是否已经存在授权信息,首次请求只会获取到上一个filter存放的匿名信息

  1. ExceptionTranslationFilter

这个fitler会全局处理下游抛出的异常,如果抛出的异常是AccessDeniedException,且是匿名用户的话会跳到指定的登陆页面

  1. FilterSecurityInterceptor

如果没抛出异常,会将登陆的用户鉴权信息存储到SecurityContextHolder,供一次访问使用

匿名用户


image.png

相关文章

网友评论

      本文标题:Spring cloud oauth2 研究--授权方式分析

      本文链接:https://www.haomeiwen.com/subject/jvhqaqtx.html