OAuth 2.0 的四种认证模式

OAuth 2四种模式

  • 授权码(authorization-code)
  • 隐藏式(implicit)
  • 密码式(password):
  • 客户端凭证(client credentials)

1.授权码模式

应用场景:第三方账号登录,接入微信开放平台,需要申请appid ,appsecret相当于clientId,secret。

1.1操作步骤:

B网站的用户可以登录 A网站,A网站可以在用户同意授权以后去B网站拿一些开放的数据

弹出B网站登录二维码网址:

http://B.com/oauth/authorize?response_type=code&client_id=client1&redirect_uri=https://www.baidu.com

请求access_token网址:

http://B.com/oauth/token?grant_type=authorization_code&code=LGj3F5&client_id=client1&client_secret=123456&redirect_uri=https://www.baidu.com&scope=all

获取token样式:

{
    "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0ZXN0IjoiaGVsbG8iLCJzY29wZSI6WyJhbGwiXSwiZGV0YWlscyI6eyJyZW1vdGVBZGRyZXNzIjoiMDowOjA6MDowOjA6MDoxIiwic2Vzc2lvbklkIjoiQjZGNTZBNTEwRUUzMTUwOTc0RjZDN0Q3NzgwMEJDQTkifSwiZXhwIjoxNjMxMDk0MDU1LCJ1c2VyTmFtZSI6ImFkbWluIiwiYXV0aG9yaXRpZXMiOlt7ImF1dGhvcml0eSI6IlVTRVIifV0sImp0aSI6IjA1ZWM3ZGJlLTk3NWUtNGMzMS1hYzA2LWI0NTZkNDM3ZDBhNyIsImNsaWVudF9pZCI6ImNsaWVudDEifQ.F4PmzFOUnyXjAsXKzCS8upagfH7q2_KVHcdOkxXHA1eBNdGi5llItUDeI2Iug0waPWh1wsM7FdusM9jWL5lbsqdWR0atXzlRX8OIldCvJl4PdK9b2YMhBFqcZyG5ch-ZdtCMC9JPymVx5wrVT0hFDqwSadrdJhUC6FHFlso3U_AmdmpLzpfFgvSJAxB-C-fmpYGeR2UTxHPMC1IEewNbx00Q05adSUSIi2Qs5zMtl5T5UYdVlWJyaDYciEAjGaqd4US_fQi-vkIYKMn9Rj3IzK_TCUfsxjGg1Z5pCpO-loVJ5ooABixrg_F6IOXYlSYhAZLpeV9zcyVEzRpIiqzRPA",
    "token_type": "bearer",
    "refresh_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0ZXN0IjoiaGVsbG8iLCJzY29wZSI6WyJhbGwiXSwiYXRpIjoiMDVlYzdkYmUtOTc1ZS00YzMxLWFjMDYtYjQ1NmQ0MzdkMGE3IiwiZGV0YWlscyI6eyJyZW1vdGVBZGRyZXNzIjoiMDowOjA6MDowOjA6MDoxIiwic2Vzc2lvbklkIjoiQjZGNTZBNTEwRUUzMTUwOTc0RjZDN0Q3NzgwMEJDQTkifSwiZXhwIjoxNjMxMDk5MDU1LCJ1c2VyTmFtZSI6ImFkbWluIiwiYXV0aG9yaXRpZXMiOlt7ImF1dGhvcml0eSI6IlVTRVIifV0sImp0aSI6ImMxYzJhMTI4LWRiYzAtNGQ4Yi1iZTM4LWQzMjE1YWI4NmExMiIsImNsaWVudF9pZCI6ImNsaWVudDEifQ.YaS2h-jb9gJWsc6ftMkQB9cbPfusrZsQgTielWVwODkNHfiuUfL-ORrlDv5DNkhaeLk-DIDD6y-rpZgJKj2dS_ht_tIMelAlKgxWWjDKYhlY4GRxF9WjqZmma1IXkbLbrpBktu_Vc2JuexG0o9QFXOHyMKfZ6ouwZPHsw_eGN-XgJuhdOByizITEExCs9EDw5kGi6ph1ZCrx5NQvHcLzoPt0iItDY3CvoYLURN61JgdJoY5EonHuQi0ZiMUl8daSvu99-2Cm-Fvd_jsRarHKNWgvtdOmrAwgzkYvLipetT60zrkNGwkyFCW6_uLSYVCm92m9qtxpq42VjY6tVqx4kA",
    "expires_in": 3599,
    "scope": "all",
    "userName": "admin",
    "authorities": [
        {
            "authority": "USER"
        }
    ],
    "jti": "05ec7dbe-975e-4c31-ac06-b456d437d0a7"
}

 1.2 代码实现:

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.4</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>spring-secret-demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>spring-secret-demo</name>
    <description>spring-secret-demo</description>
    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>2020.0.3</spring-cloud.version>
    </properties>
    <dependencies>
        <!--<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter</artifactId>
        </dependency>-->

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-security</artifactId>
            <version>2.0.1.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-oauth2</artifactId>
            <version>2.2.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
    </dependencies>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

授权服务类:

package com.example.springsecretdemo.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
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.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;
import org.springframework.security.oauth2.provider.token.TokenEnhancerChain;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.store.JwtTokenStore;
import org.springframework.security.oauth2.provider.token.store.KeyStoreKeyFactory;

import java.util.Arrays;
import java.util.concurrent.TimeUnit;

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

    @Autowired
    private AuthenticationManager authenticationManager;

//    @Autowired
//    private PasswordEncoder passwordEncoder;
    @Autowired
    UserDetailsService userDetailsService;

    @Bean
    public TokenStore tokenStore() {
        return new JwtTokenStore(accessTokenConverter());
    }


    @Bean
    public JwtAccessTokenConverter accessTokenConverter() {
        final JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
//    converter.setSigningKey("123");
        KeyStoreKeyFactory keyStoreKeyFactory =
                new KeyStoreKeyFactory(new ClassPathResource("mytest.jks"), "mypass".toCharArray());
        converter.setKeyPair(keyStoreKeyFactory.getKeyPair("mytest"));
        converter.setAccessTokenConverter(new CustomerAccessTokenConverter());
        return converter;
    }

    /**
     * 注入自定义token生成方式
     *
     * @return
     */
    @Bean
    public TokenEnhancer customerEnhancer() {
        return new CustomTokenEnhancer();
    }

    /**
     * 配置客户端详情服务
     * 客户端详细信息在这里进行初始化,你能够把客户端详情信息写死在这里或者是通过数据库来存储调取详情信息
     *
     * @param clients
     * @throws Exception
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
                //用于标识用户ID
                .withClient("client1")
                //授权方式
                .authorizedGrantTypes("authorization_code","implicit","password","client_credentials ", "refresh_token")
                //授权范围"test"
                .scopes("all")
                .redirectUris("https://www.baidu.com")
                .accessTokenValiditySeconds(3600)
                .refreshTokenValiditySeconds(8600)
                //客户端安全码,secret密码配置从 Spring Security 5.0开始必须以 {bcrypt}+加密后的密码 这种格式填写;
                .secret(PasswordEncoderFactories.createDelegatingPasswordEncoder().encode("123456"));

    }

    /**
     * 用来配置令牌端点(Token Endpoint)的安全约束.
     *
     * @param security
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        /* 配置token获取合验证时的策略 */
        security.tokenKeyAccess("permitAll()").checkTokenAccess("isAuthenticated()").allowFormAuthenticationForClients();
    }

    /**
     * 用来配置授权(authorization)以及令牌(token)的访问端点和令牌服务(token services)
     *
     * @param endpoints
     * @throws Exception
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        // 配置tokenStore
//    endpoints.authenticationManager(authenticationManager).tokenStore(tokenStore())
//            .accessTokenConverter(accessTokenConverter()).userDetailsService(userDetailsService);
        //指定认证管理器
        endpoints.authenticationManager(authenticationManager)
                //refresh_token是否重复使用,false不允许
                .reuseRefreshTokens(false);
        //指定token存储位置
        endpoints.tokenStore(tokenStore());

        endpoints.accessTokenConverter(accessTokenConverter());
        endpoints.userDetailsService(userDetailsService);
        //自定义token生成方式
        TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
        tokenEnhancerChain.setTokenEnhancers(Arrays.asList(customerEnhancer(), accessTokenConverter()));
        endpoints.tokenEnhancer(tokenEnhancerChain);

        // 配置TokenServices参数
        DefaultTokenServices tokenServices = (DefaultTokenServices) endpoints.getDefaultAuthorizationServerTokenServices();
        tokenServices.setTokenStore(endpoints.getTokenStore());
        tokenServices.setSupportRefreshToken(true);
        tokenServices.setClientDetailsService(endpoints.getClientDetailsService());
        tokenServices.setTokenEnhancer(endpoints.getTokenEnhancer());
        tokenServices.setAccessTokenValiditySeconds((int) TimeUnit.DAYS.toSeconds(1));//一天
        endpoints.tokenServices(tokenServices);

        //设置允许请求的方式

        endpoints.allowedTokenEndpointRequestMethods(HttpMethod.GET,HttpMethod.POST);
    }
}

自定义token生成类

package com.example.springsecretdemo.config;

import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.oauth2.provider.token.DefaultAccessTokenConverter;
import org.springframework.security.oauth2.provider.token.DefaultUserAuthenticationConverter;

import java.util.LinkedHashMap;
import java.util.Map;

/**
 * 自定义CustomerAccessTokenConverter 这个类的作用主要用于AccessToken的转换,
 * 默认使用DefaultAccessTokenConverter 这个装换器
 * DefaultAccessTokenConverter有个UserAuthenticationConverter,这个转换器作用是把用户的信息放入token中,
 * 默认只是放入user_name
 **/
public class CustomerAccessTokenConverter extends DefaultAccessTokenConverter {

    public CustomerAccessTokenConverter() {
        super.setUserTokenConverter(new CustomerUserAuthenticationConverter());
    }

    private class CustomerUserAuthenticationConverter extends DefaultUserAuthenticationConverter {
        @Override
        public Map<String, ?> convertUserAuthentication(Authentication authentication) {
            LinkedHashMap<String, Object> response = new LinkedHashMap<>();
            response.put("details", authentication.getDetails());
            response.put("test", "hello");
            if (authentication.getAuthorities() != null && !authentication.getAuthorities().isEmpty()) {
                response.put("authorities", AuthorityUtils.authorityListToSet(authentication.getAuthorities()));
            }
            return response;
        }
    }
}
package com.example.springsecretdemo.config;

import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.token.TokenEnhancer;

import java.util.HashMap;
import java.util.Map;

/**
 * 自定义token生成携带的信息
 **/
public class CustomTokenEnhancer implements TokenEnhancer {
    @Override
    public OAuth2AccessToken enhance(OAuth2AccessToken oAuth2AccessToken, OAuth2Authentication oAuth2Authentication) {
        final Map<String, Object> additionalInfo = new HashMap<>();
        //获取登录信息
        UserDetails user = (UserDetails) oAuth2Authentication.getUserAuthentication().getPrincipal();
        additionalInfo.put("userName", user.getUsername());
        additionalInfo.put("authorities", user.getAuthorities());
        ((DefaultOAuth2AccessToken) oAuth2AccessToken).setAdditionalInformation(additionalInfo);
        return oAuth2AccessToken;
    }
}
package com.example.springsecretdemo.config;

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.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.factory.PasswordEncoderFactories;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;

/**
 * 配置spring security
 **/
@EnableWebSecurity//开启权限验证
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    /**
     * 配置这个bean会在做AuthorizationServerConfigurer配置的时候使用
     *
     * @return
     * @throws Exception
     */
    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    /**
     * 配置用户
     * 使用内存中的用户,实际项目中,一般使用的是数据库保存用户,具体的实现类可以使用JdbcDaoImpl或者JdbcUserDetailsManager
     *
     * @return
     */
    @Bean
    @Override
    protected UserDetailsService userDetailsService() {
        InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
        manager.createUser(User.withUsername("admin").password(PasswordEncoderFactories.createDelegatingPasswordEncoder().encode("admin")).authorities("USER").build());
        return manager;
    }

//  @Override
//  protected void configure(HttpSecurity http) throws Exception {
//    http.requestMatchers().anyRequest().and().authorizeRequests().antMatchers("/oauth/*").permitAll();
//  }

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

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.formLogin().permitAll().and().authorizeRequests()
                .antMatchers("/oauth/**").permitAll()
                .antMatchers("/actuator/**").permitAll()
                .anyRequest().authenticated()
                .and().logout().permitAll()
                .and().csrf().disable();
    }
}
package com.example.springsecretdemo.dao;

import com.example.springsecretdemo.entity.User;
import org.springframework.stereotype.Repository;

@Repository
public class UserRepository {
    public User getById(String username) {
        return new User(username,"user");
    }
}

代码地址:

https://gitee.com/itwangfl/spring-boot-demo.git

访问资源地址:

http://localhost:8002/user/list?access_token=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0ZXN0IjoiaGVsbG8iLCJzY29wZSI6WyJhbGwiXSwiZGV0YWlscyI6eyJjbGllbnRfc2VjcmV0IjoiMTIzNDU2IiwiZ3JhbnRfdHlwZSI6InBhc3N3b3JkIiwiY2xpZW50X2lkIjoiY2xpZW50MSIsInVzZXJuYW1lIjoiYWRtaW4ifSwiZXhwIjoxNjMxMDkwOTgyLCJ1c2VyTmFtZSI6ImFkbWluIiwiYXV0aG9yaXRpZXMiOlt7ImF1dGhvcml0eSI6IlVTRVIifV0sImp0aSI6ImY2YzBjYmUxLTFkOTEtNGRhNy04MTJlLTc5MzU5OTFlOWZjNSIsImNsaWVudF9pZCI6ImNsaWVudDEifQ.QvCHTnhwdW-PsPIdUYF4RUmdoHcumg4u6u_eaKXWs7_DPQSkRV0iE5q02ca7xt5S1dwKPqXXk8Pc3fLy3bEf_1wWlpWu8yf5oN1p20LqMQZQYfHih4syBOLe7j6a1UzkW48OpZvhvX1jlAhwlmH25Hdiy4D6K1PgYB1wKTKTvDY5Ds9v2hm119xkVsQrsGfoa9r_BYc6s-UNRDOeG-cORsAHWzQVUFi-FvRsg4ixt53iMi8VJCbPSwfZsHSznx0PE-KLQlxudukZ-uiJjZCLNL2V_wfaBay77op4ctr-aFHBAOfYbQqkykVODuT_DIibUuqwt3L08RJitoGGtdQzVg

密码方式获取token:

http://localhost:8001/oauth/token?grant_type=password&client_id=client1&client_secret=123456&username=admin&password=admin

code方式获取token:

获取code

http://localhost:8001/oauth/authorize?response_type=code&client_id=client1&redirect_uri=https://baidu.com

获取到的:https://www.baidu.com/?code=435zh7

获取token

http://localhost:8001/oauth/token?grant_type=authorization_code&code=435zh7&client_id=client1&client_secret=123456&redirect_uri=https://www.baidu.com&scope=all

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值