• Spring Boot OAuth2从0到1


    二、开发流程

    1.1、知识来源

    OAuth2开发指导:https://projects.spring.io/spring-security-oauth/docs/oauth2.html
    Spring Boot下的OAuth2使用:https://docs.spring.io/spring-security-oauth2-boot/docs/2.2.0.RELEASE/reference/html5/
    Spring Cloud Security:
    博客参考1:https://www.cnblogs.com/Irving/p/9430460.html

    1.2、使用Spring Boot初始化项目工具,勾选Web

    当前版本是:Spring Boot2.2.0.RELEASE,Spring Cloud Hoxton.M3
    由于Spring Security OAuth2并不在Spring Boot中维护,所以不能在Spring Boot中自动引入依赖
    但是Spring Cloud中好像维护了Spring Security OAuth2的版本
    生成的工程自动引入了spring-cloud-starter-oauth2依赖,该依赖中包含了spring-security-oauth2-autoconfigure依赖

    手动加入spring-security-oauth2-autoconfigure依赖

    1.3、参照Spring Boot下OAuth2的指引

    增加@EnableAuthorizationServer注解

    @EnableAuthorizationServer
    @SpringBootApplication
    public class SimpleAuthorizationServerApplication {
        public static void main(String[] args) {
            SpringApplication.run(SimpleAuthorizationServerApplication, args);
        }
    }
    
    • 定义一个客户端和密码
    security:
      oauth2:
        client:
          client-id: first-client
          client-secret: noonewilleverguess
    

    获取token:
    curl client:pwd@localhost:8080/oauth/token -d grant_type=client_credentials -d scope=any

    1.4、根据【OAuth2开发指导】配置AuthorizationServerConfigurerAdapter中客户端明细

    @Configuration
    public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
    	// 配置客户端明细
    	@SneakyThrows
    	@Override
    	public void configure(ClientDetailsServiceConfigurer clients) {
    		// 1、基于内存的客户端明细
    		clients.inMemory().withClient("client")// 允许访问的客户端
    				.secret("{noop}pwd")// 密码
    				.authorizedGrantTypes("refresh_token", "password", "client_credentials")// 允许的授权类型
    				.scopes("webclient", "mobileclient");// 引用程序作用域
    	}
    }
    

    然后通过客户端密码模式获取token
    curl client:pwd@localhost:8080/oauth/token -d grant_type=client_credentials -d scope=webclient
    注意,新版本的Spring Security5中,密码增加了前缀{noop},可以指定密码的加密内容,只有服务端需要加该前缀,前端传的是加密后的密码

    修改自定义实现获取客户端明细
    分别引入jdbc、mysql、druid依赖,这里借助了JdbcClientDetailsService这个实现,进行了扩展,稍微修改了SQL,以及加入了缓存机制
    注意一点:这里的sql中,使用了CONCAT('{noop}',client_secret),代表这里的密码是明文存储的,以后有需要可以修改这里
    再次通过客户端密码模式获取token,客户端账号密码、scope,按数据库存储的输入,即可获取得到
    curl client:pwd@localhost:8080/oauth/token -d grant_type=client_credentials -d scope=webclient

    1.5、根据【Spring Boot下的OAuth2使用】配置使用password模式下,用户的账号信息获取

    参考https://docs.spring.io/spring-security-oauth2-boot/docs/2.2.0.RELEASE/reference/html5/#oauth2-boot-authorization-server-password-grant-authentication-configuration
    要使用OAuth2的Password模式,有多种方式,由于我们使用AuthorizationServerConfigurerAdapter,符合1.7.3所说,如下即可实现

    • AuthorizationServerEndpointsConfigurer绑定Security的authenticationManager
    • 然后创建一个UserDetailsService的Bean
    @Configuration
    public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
    	@Autowired
    	private AuthenticationConfiguration authenticationConfiguration;
    
    	@Override
    	public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
    		endpoints.authenticationManager(authenticationConfiguration.getAuthenticationManager())
    	}
    }
    
    @Service
    public class HopeUserDetailsService implements UserDetailsService {
    	@Override
    	@SneakyThrows
    	public UserDetails loadUserByUsername(String username) {
    		
    		Collection<? extends GrantedAuthority> authorities = AuthorityUtils.createAuthorityList("role1");
    		return new HopeUser(1, 1, 1, "hope", "{noop}hope", true, true, true, true,authorities );
    	}
    }
    

    curl client:pwd@localhost:8080/oauth/token -d grant_type=password -d scope=webclient -d username=hope -d password=hope
    即可获取到token

    1.7、测试验证token端点

    默认验证token端点是/oauth/check_token
    先配置验证端点完全开放

    @Configuration
    public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
    	@Override
    	public void configure(AuthorizationServerSecurityConfigurer oauthServer) {
    		oauthServer
    				.checkTokenAccess("permitAll()");
    	}
    }
    

    curl client:pwd@localhost:8080/oauth/check_token -d token=8313a24b-c0d2-437b-afb0-0bb4fd440f47
    返回令牌中携带信息

    1.8、保护资源服务器

    使用Spring Boot初始化项目工具,勾选Web
    手动加入spring-security-oauth2-autoconfigure依赖
    使用@EnableResourceServer注解
    配置文件如下

    security:
      oauth2:
        resource:
          token-info-uri: http://localhost:8080/oauth/check_token
        client:
          # 配置后才能认证,生产环境建议设置独立的客户端信息
          client-id: test
          client-secret: test
          scope: server
    

    注意,需要开放授权服务器的check_token端点

    @Configuration
    public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
    	@Override
    	public void configure(AuthorizationServerSecurityConfigurer oauthServer) {
    		oauthServer
    				.checkTokenAccess("isAuthenticated()");
    	}
    }
    

    此时,直接访问资源提示没有权限
    访问授权服务器,获取token,使用bear token访问这个受保护资源,访问成功!

    1.9、使用JWT Token

    spring-security-oauth2-autoconfigure已包含jwt依赖,所以不需要处理依赖
    授权服务器:
    通过AuthorizationServerEndpointsConfigurer配置.accessTokenConverter(jwtAccessTokenConverter),配置一个JwtAccessTokenConverter即可,具体方法下面讨论
    资源服务器:
    配置application配置文件即可。

    2者成组配置:

    方式1:对称加密
    授权服务器:

    @Bean
    public JwtAccessTokenConverter jwtAccessTokenConverter() {
    	JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
    	converter.setSigningKey("micosrv_signing_key");
    	return converter;
    }
    

    资源服务器:

    security:
      oauth2:
        resource:
          jwt:
            key-value: micosrv_signing_key
    

    方式2:非对称加密(自行赋值)
    授权服务器:

    config:
        oauth2:
            # openssl genrsa -out jwt.pem 2048
            # openssl rsa -in jwt.pem
            privateKey: |
                -----BEGIN RSA PRIVATE KEY-----
                MIIEowIBAAKCAQEAm4irSNcR7CSSfXconxL4g4M4j34wTWdTv93ocMn4VmdB7rCB
                U/BlxXtBUf/cgLIgQhQrAPszSZSmxiEXCOkGPr4aQBQuPgmNIR95Dhbzw/ZN0Bne
                cAt3ZfkkDBHv8kH3kR/jYGTdwrxKeDgXGljNsTRhbjuASxPG/Z6gU1yRPCsgc2r8
                NYnztWGcDWqaobqjG3/yzFmusoAboyV7asIpo4yk378LmonDNwxnOOTb2Peg5Pee
                lwfOwJPbftK1VOOt18zA0cchw6dHUzq9NlB8clps/VdBap9BxU3/0YoFXRIc18ny
                zrWo2BcY2KQqX//AJC3OAfrfDmo+BGK8E0mp8wIDAQABAoIBAENp64P45GXMPEpx
                eYPpfxnRqJRZh6olHSHOl087243n16YTjxrI2fPMxrU6B2Mo0d6SS0lzl/lOmzLJ
                aOiNyA0t7MbVeG2fSjKPJ7M5s5K+kV+fttAtyCTE5iDtLWl9ukaG4dEIJy6e2lBd
                T3Y2A4HJSGm1FJh2DAwl0ywOtUy0X6ki9DgXVAaCGDuoU25Rhun64dh802DZbEEJ
                LdorIyeJ0ovCZyNvhlZRYkAOPy3k88smYl2jE/AbZ7pCKz/XggDcjNsERm2llaa3
                pNTAZQUlHu0BQrCn6J9BxtMPyduiyrE+JYqTwnYhWQ5QRe/2J8O3t0eIK9TfUQpJ
                DrZf00ECgYEAy/sLX8UCmERwMuaQSwoM0BHTZIc0iAsgiXbVOLua9I3Tu/mXOVdH
                TikjdoWLqM62bA9dN/oqzHDwvqCy6zwamjFVSmJUejf5v+52Qj64leOmDX/RC4ne
                L08N1nP/Y4X24Y/5zq18qvVlhOMDdydzayJFrGhkQKhJg58pRUIdenECgYEAwzLC
                Awr3LeUlHa+d2O6siJVmljTc8lT+qX4TvqTDH8rAC/EyKMNaTjaX6mWosZZ7qYXv
                EMxvQzTEzUHRXrCGlhbX8xiBlWnvpghF2GJEvP9WaU/+OCr0gItRSLPDuZ6ctzKb
                3QkBEiC8ODyPRKzlA67D23S3KJB067IUV81h9KMCgYBXUqmT3is2NFYz9DBhb3P8
                vyTYLGl4tArBznWJTAcSGoVCO59ZlNuZwlLEMnePVK8To6AsjpQz4UWu1ezCd4CL
                8gKpTV8M01m/qL5HrcInqMU1kjpTzjmn1xf9brsuR/NgrNoseGieZ1+GfAjHwcPP
                YWSiYi5I38JY7pIkbCFigQKBgAnVtty8YrPXRcV3IbbaX6sKC/8pbrBvA926Unha
                iNJDPuXbIzHWleg26/SNZrB76oMiEmeARWLXd8r3s/rXXhCV2g+PfofurHprFEnQ
                ubHkE5B+zUo7L9KCMng9RnFFwpOgYyYB3CHzsEgNFRLauzcySP/3o3rRvHJbqJa7
                7GGNAoGBAKSBn4zq0iNWI2BUBb90icMsHEneiydGtFcEl3/Sz8vmjFZn0sjRbGoY
                gmP9LlQ+o7xRiJ/LTesi5BA6zCGrcdp0aeyJzCRbFc3WqjGeyLbfx1sJVVB6PnvS
                iKvvCOJq6kl3/opO+ybqJ8dzkEyoj8K4+fcX1+U6eW2w+vSpOosG
                -----END RSA PRIVATE KEY-----
            # openssl rsa -in jwt.pem -pubout
            publicKey: |
                -----BEGIN PUBLIC KEY-----
                MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAm4irSNcR7CSSfXconxL4
                g4M4j34wTWdTv93ocMn4VmdB7rCBU/BlxXtBUf/cgLIgQhQrAPszSZSmxiEXCOkG
                Pr4aQBQuPgmNIR95Dhbzw/ZN0BnecAt3ZfkkDBHv8kH3kR/jYGTdwrxKeDgXGljN
                sTRhbjuASxPG/Z6gU1yRPCsgc2r8NYnztWGcDWqaobqjG3/yzFmusoAboyV7asIp
                o4yk378LmonDNwxnOOTb2Peg5PeelwfOwJPbftK1VOOt18zA0cchw6dHUzq9NlB8
                clps/VdBap9BxU3/0YoFXRIc18nyzrWo2BcY2KQqX//AJC3OAfrfDmo+BGK8E0mp
                8wIDAQAB
                -----END PUBLIC KEY-----
    
    @Bean
    public JwtAccessTokenConverter jwtAccessTokenConverter() {
    	// 自行生成公私钥
    	JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
    	converter.setSigningKey(privateKey);
    	converter.setVerifierKey(publicKey);
    	return converter;
    }
    

    资源服务器:

    security:
      oauth2:
        resource:
          jwt:
            key-value: 
                -----BEGIN PUBLIC KEY-----
                MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAm4irSNcR7CSSfXconxL4
                g4M4j34wTWdTv93ocMn4VmdB7rCBU/BlxXtBUf/cgLIgQhQrAPszSZSmxiEXCOkG
                Pr4aQBQuPgmNIR95Dhbzw/ZN0BnecAt3ZfkkDBHv8kH3kR/jYGTdwrxKeDgXGljN
                sTRhbjuASxPG/Z6gU1yRPCsgc2r8NYnztWGcDWqaobqjG3/yzFmusoAboyV7asIp
                o4yk378LmonDNwxnOOTb2Peg5PeelwfOwJPbftK1VOOt18zA0cchw6dHUzq9NlB8
                clps/VdBap9BxU3/0YoFXRIc18nyzrWo2BcY2KQqX//AJC3OAfrfDmo+BGK8E0mp
                8wIDAQAB
                -----END PUBLIC KEY-----
    

    方式3:非对称加密(通过token_key端点)
    授权服务器:
    在方式2的基础上,需要配置开放token_key端口,在AuthorizationServerConfigurerAdapter中配置

    @Override
    public void configure(AuthorizationServerSecurityConfigurer oauthServer) {
    	oauthServer.tokenKeyAccess("permitAll()");
    }
    

    资源服务器:

    security:
      oauth2:
        resource:
          jwt:
            key-uri: http://localhost:8080/oauth/token_key
    

    1.10、token传递

    客户端添加依赖:alibaba discovery,alibaba config,hystricx断路器,feign依赖,spring-cloud-starter-security
    并且管理版本:spring-cloud-alibaba-dependencies 0.9.0.RELEASE
    使用@SpringCloudApplication注解,开启Cloud App,支持服务注册于发现和断路器
    使用@EnableFeignClients注解,支持Feign客户端
    配置一个OAuth2FeignRequestInterceptor拦截器,通过AccessTokenContextRelay来传递token
    AccessTokenContextRelay在@EnableOAuth2Client中有配置,但是我们这里不作为一个OAuth2客户端来看,可以自己创建一个AccessTokenContextRelay
    调用Feign,即可看到token是否能正常传递

    1.11、JWT过于庞大,应该使用令牌内省这个概念

    使用原来的令牌,在资源服务端访问授权服务器的时候,进行缓存,下次访问的时候取缓存
    参考文章:
    冷总:https://cloud.tencent.com/developer/article/1435727
    冷总:https://my.oschina.net/giegie/blog/3023768
    https://my.oschina.net/u/2518341/blog/3021642
    思路

    • 参考冷总第二篇文章,使用JWT,然后自己调用check_token,再解析
    • 使用原始的访问check_token方案,注入自己的RestTemplate,增加拦截器,进行缓存和处理

    1.12、密码的加密

    pig中应该是网关进行了加密和解密,OAuth2服务器接收的应该是原生密码,然后通过BCRYPT加密,与数据库中已经过BCRYPT加密的密码进行对比

    1.13、check_token添加自定义信息

    返回值是通过AccessTokenConverter来进行转换的,我继承DefaultAccessTokenConverter,并在convertAccessToken中,增加自定义的信息
    授权服务器:在AuthorizationServerConfigurerAdapter中注入AccessTokenConverter
    资源服务器:在ResourceServerTokenServices中注入AccessTokenConverter

  • 相关阅读:
    Fibonacci数列2
    足球队
    网页导航
    Catenyms
    某种密码
    大逃亡
    球的序列
    圆内三角形统计
    最小平方数

  • 原文地址:https://www.cnblogs.com/LiveYourLife/p/12362210.html
Copyright © 2020-2023  润新知