OAuth2认证流程

微信扫码认证,这是一种第三方认证的方式,这种认证方式是基于OAuth2协议实现,
OAUTH协议为用户资源的授权提供了一个安全的、开放而又简易的标准。同时,任何第三方都可以使用OAUTH认证服务,任何服务提供商都可以实现自身的OAUTH认证服务,因而OAUTH是开放的。业界提供了OAUTH的多种实现如PHP、JavaScript,Java,Ruby等各种语言开发包,大大节约了程序员的时间,因而OAUTH是简易的。互联网很多服务如Open API,很多大公司如Google,Yahoo,Microsoft等都提供了OAUTH认证服务,这些都足以说明OAUTH标准逐渐成为开放资源授权的标准。
Oauth协议目前发展到2.0版本,1.0版本过于复杂,2.0版本已得到广泛应用。
参考:https://baike.baidu.com/item/oAuth/7153134?fr=aladdin
Oauth协议:https://tools.ietf.org/html/rfc6749
下边分析一个Oauth2认证的例子
在这里插入图片描述
接下来认识Oauth2.0的认证流程,如下:
引自Oauth2.0协议rfc6749 https://tools.ietf.org/html/rfc6749
在这里插入图片描述
Oauth2包括以下角色:
1、客户端
本身不存储资源,需要通过资源拥有者的授权去请求资源服务器的资源,比如:手机客户端、浏览器等。
2、资源拥有者
通常为用户,也可以是应用程序,即该资源的拥有者。
A表示 客户端请求资源拥有者授权。
B表示 资源拥有者授权客户端访问自己的用户信息。
3、授权服务器(也称认证服务器)
认证服务器对资源拥有者进行认证,还会对客户端进行认证并颁发令牌。
C 客户端网站携带授权码请求认证。
D认证通过颁发令牌。
4、资源服务器
存储资源的服务器。
E表示客户端网站携带令牌请求资源服务器获取资源。
F表示资源服务器校验令牌通过后提供受保护资源。
OAuth2的授权模式
Spring Security支持OAuth2认证,OAuth2提供授权码模式、密码模式、简化模式、客户端模式等四种授权模式,前边举的微信扫码登录的例子就是基于授权码模式,这四种模式中授权码模式和密码模式应用较多,本实例使用Spring Security演示授权码模式、密码模式,其余两种请自行查阅相关资料。
授权码模式
OAuth2的几个授权模式是根据不同的应用场景以不同的方式去获取令牌,最终目的是要获取认证服务颁发的令牌,最终通过令牌去获取资源。
授权码模式简单理解是使用授权码去获取令牌,要想获取令牌先要获取授权码,授权码的获取需要资源拥有者亲自授权同意才可以获取。
下图是授权码模式的交互图:
在这里插入图片描述
1、用户打开浏览器。
2、通过浏览器访问客户端网站。
3、用户通过浏览器向认证服务请求授权,请求授权时会携带客户端的URL,此URL为下发授权码的重定向地址。
4、认证服务向资源拥有者返回授权页面。
5、资源拥有者亲自授权同意。
6、通过浏览器向认证服务发送授权同意。
7、认证服务向客户端地址重定向并携带授权码。
8、客户端网站收到授权码。
9、客户端携带授权码向认证服务申请令牌。
10、认证服务向客户端颁发令牌。
授权码模式测试
1、新建 AuthorizationServer.java、TokenConfig.java到认证服务的config包下。
说明“:AuthorizationServer用 @EnableAuthorizationServer 注解标识并继承AuthorizationServerConfigurerAdapter来配置OAuth2.0 授权服务器。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;

import javax.annotation.Resource;

/**
 * @description 授权服务器配置
 */
 @Configuration
 @EnableAuthorizationServer
 public class AuthorizationServer extends AuthorizationServerConfigurerAdapter {

  @Resource(name="authorizationServerTokenServicesCustom")
  private AuthorizationServerTokenServices authorizationServerTokenServices;

 @Autowired
 private AuthenticationManager authenticationManager;

  //客户端详情服务
  @Override
  public void configure(ClientDetailsServiceConfigurer clients)
          throws Exception {
        clients.inMemory()// 使用in-memory存储
                .withClient("XcWebApp")// client_id
                .secret("XcWebApp")//客户端密钥
//                .secret(new BCryptPasswordEncoder().encode("XcWebApp"))//客户端密钥
                .resourceIds("xuecheng-plus")//资源列表
                .authorizedGrantTypes("authorization_code", "password","client_credentials","implicit","refresh_token")// 该client允许的授权类型authorization_code,password,refresh_token,implicit,client_credentials
                .scopes("all")// 允许的授权范围
                .autoApprove(false)//false跳转到授权页面
                //客户端接收授权码的重定向地址
                .redirectUris("http://www.51xuecheng.cn");
  }


  //令牌端点的访问配置
  @Override
  public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
   endpoints
           .authenticationManager(authenticationManager)//认证管理器
           .tokenServices(authorizationServerTokenServices)//令牌管理服务
           .allowedTokenEndpointRequestMethods(HttpMethod.POST);
  }

  //令牌端点的安全配置
  @Override
  public void configure(AuthorizationServerSecurityConfigurer security){
   security
           .tokenKeyAccess("permitAll()")                    //oauth/token_key是公开
           .checkTokenAccess("permitAll()")                  //oauth/check_token公开
           .allowFormAuthenticationForClients()				//表单认证(申请令牌)
   ;
  }



 }

1)ClientDetailsServiceConfigurer:用来配置客户端详情服务(ClientDetailsService),
随便一个客户端都可以随便接入到它的认证服务吗?答案是否定的,服务提供商会给批准接入的客户端一个身份,用于接入时的凭据,有客户端标识和客户端秘钥,在这里配置批准接入的客户端的详细信息。
2)AuthorizationServerEndpointsConfigurer:用来配置令牌(token)的访问端点和令牌服务(token services)。
3)AuthorizationServerSecurityConfigurer:用来配置令牌端点的安全约束
2、TokenConfig为令牌策略配置类
暂时先使用InMemoryTokenStore在内存存储令牌,令牌的有效期等信息配置如下:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenEnhancerChain;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;

import java.util.Arrays;

/**
 * token 配置
 **/
@Configuration
public class TokenConfig {

    @Autowired
    TokenStore tokenStore;

    @Bean
    public TokenStore tokenStore() {
        //使用内存存储令牌(普通令牌)
        return new InMemoryTokenStore();
    }

    //令牌管理服务
    @Bean(name="authorizationServerTokenServicesCustom")
    public AuthorizationServerTokenServices tokenService() {
        DefaultTokenServices service=new DefaultTokenServices();
        service.setSupportRefreshToken(true);//支持刷新令牌
        service.setTokenStore(tokenStore);//令牌存储策略
        service.setAccessTokenValiditySeconds(7200); // 令牌默认有效期2小时
        service.setRefreshTokenValiditySeconds(259200); // 刷新令牌默认有效期3天
        return service;
    }


}

3、配置认证管理bean

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
/**
 * @description 安全管理配置
 */
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true,prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

@Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }
    //配置用户信息服务
@Bean
public UserDetailsService userDetailsService() {
    //这里配置用户信息,这里暂时使用这种方式将用户存储在内存中
    InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
            manager.createUser(User.withUsername("zhangsan").password("123").authorities("p1").build());
    manager.createUser(User.withUsername("lisi").password("456").authorities("p2").build());
    return manager;
}

    @Bean
    public PasswordEncoder passwordEncoder() {
        //密码为明文方式
        return NoOpPasswordEncoder.getInstance();
    }

    //配置安全拦截机制
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                .antMatchers("/r/**").authenticated()//访问/r开始的请求需要认证通过
                .anyRequest().permitAll()//其它请求全部放行
                .and()
                .formLogin().successForwardUrl("/login-success");//登录成功跳转到/login-success
                http.logout().logoutUrl("/logout");//退出地址
    }
}

重启认证服务
1、get请求获取授权码
地址: http://localhost:63070/auth/oauth/authorize?client_id=XcWebApp&response_type=code&scope=all&redirect_uri=http://www.51xuecheng.cn
参数列表如下:
•client_id:客户端准入标识。
•response_type:授权码模式固定为code。
•scope:客户端权限。
•redirect_uri:跳转uri,当授权码申请成功后会跳转到此地址,并在后边带上code参数(授权码)。
输入账号zhangsan、密码123登录成功,输入/oauth/authorize?client_id=XcWebApp&response_type=code&scope=all&redirect_uri=http://www.51xuecheng.cn
显示授权页面
在这里插入图片描述
授权“XcWebApp”访问自己的受保护资源?
选择同意。
2、请求成功,重定向至http://www.51xuecheng.cn/?code=授权码,比如:http://www.51xuecheng.cn/?code=Wqjb5H
3、使用httpclient工具post申请令牌
/oauth/token?client_id=XcWebApp&client_secret=XcWebApp&grant_type=authorization_code&code=授权码&redirect_uri=http://www.51xuecheng.cn/
参数列表如下
•client_id:客户端准入标识。
•client_secret:客户端秘钥。
•grant_type:授权类型,填写authorization_code,表示授权码模式
•code:授权码,就是刚刚获取的授权码,注意:授权码只使用一次就无效了,需要重新申请。
•redirect_uri:申请授权码时的跳转url,一定和申请授权码时用的redirect_uri一致。
httpclient脚本如下:


### 授权码模式
### 第一步申请授权码(浏览器请求)/oauth/authorize?client_id=XcWebApp&response_type=code&scope=all&redirect_uri=http://www.51xuecheng.cn
### 第二步申请令牌
POST localhost:63070/auth/oauth/token?client_id=XcWebApp&client_secret=XcWebApp&grant_type=authorization_code&code=CTvCrB&redirect_uri=http://www.51xuecheng.cn

申请令牌成功如下所示:

{
  "access_token": "368b1ee7-a9ee-4e9a-aae6-0fcab243aad2",
  "token_type": "bearer",
  "refresh_token": "3d56e139-0ee6-4ace-8cbe-1311dfaa991f",
  "expires_in": 7199,
  "scope": "all"
}

说明:
1、access_token,访问令牌,用于访问资源使用。
2、token_type,bearer是在RFC6750中定义的一种token类型,在携带令牌访问资源时需要在head中加入bearer 空格 令牌内容
3、refresh_token,当令牌快过期时使用刷新令牌可以再次生成令牌。
4、expires_in:过期时间(秒)
5、scope,令牌的权限范围,服务端可以根据令牌的权限范围去对令牌授权。
密码模式
密码模式相对授权码模式简单,授权码模式需要借助浏览器供用户亲自授权,密码模式不用借助浏览器,如下图:
在这里插入图片描述
1、资源拥有者提供账号和密码
2、客户端向认证服务申请令牌,请求中携带账号和密码
3、认证服务校验账号和密码正确颁发令牌。
开始测试:
1、POST请求获取令牌
/oauth/token?client_id=XcWebApp&client_secret=XcWebApp&grant_type=password&username=shangsan&password=123
参数列表如下:
•client_id:客户端准入标识。
•client_secret:客户端秘钥。
•grant_type:授权类型,填写password表示密码模式
•username:资源拥有者用户名。
•password:资源拥有者密码。
2、授权服务器将令牌(access_token)发送给client
使用httpclient进行测试

POST localhost:63070/auth/oauth/token?client_id=XcWebApp&client_secret=XcWebApp&grant_type=password&username=zhangsan&password=123
{
  "access_token": "368b1ee7-a9ee-4e9a-aae6-0fcab243aad2",
  "token_type": "bearer",
  "refresh_token": "3d56e139-0ee6-4ace-8cbe-1311dfaa991f",
  "expires_in": 6806,
  "scope": "all"
}

这种模式十分简单,但是却意味着直接将用户敏感信息泄漏给了client,因此这就说明这种模式只能用于client是我们自己开发的情况下。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值