概述
通过 Spring Security
实现 OAuth 2.0
快速导航。以下案例基于 java11
搭建。
创建 OAuth 2.0 服务
pom.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 <?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.3.4.RELEASE</version > <relativePath /> </parent > <groupId > cn.idea360</groupId > <artifactId > idc-oauth2</artifactId > <version > 0.0.1</version > <name > idc-oauth2</name > <description > Spring Boot + Security + OAuth2</description > <properties > <java.version > 11</java.version > </properties > <dependencies > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-web</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 > <exclusions > <exclusion > <groupId > org.junit.vintage</groupId > <artifactId > junit-vintage-engine</artifactId > </exclusion > </exclusions > </dependency > <dependency > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-starter-security</artifactId > </dependency > <dependency > <groupId > org.springframework.security.oauth</groupId > <artifactId > spring-security-oauth2</artifactId > <version > 2.3.4.RELEASE</version > </dependency > <dependency > <groupId > javax.xml.bind</groupId > <artifactId > jaxb-api</artifactId > <version > 2.3.0</version > </dependency > <dependency > <groupId > com.sun.xml.bind</groupId > <artifactId > jaxb-impl</artifactId > <version > 2.3.0</version > </dependency > <dependency > <groupId > com.sun.xml.bind</groupId > <artifactId > jaxb-core</artifactId > <version > 2.3.0</version > </dependency > <dependency > <groupId > javax.activation</groupId > <artifactId > activation</artifactId > <version > 1.1.1</version > </dependency > </dependencies > <build > <plugins > <plugin > <groupId > org.springframework.boot</groupId > <artifactId > spring-boot-maven-plugin</artifactId > </plugin > </plugins > </build > </project >
application.yml
认证服务配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 @Configuration @EnableAuthorizationServer public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter { @Autowired private AuthenticationManager authenticationManager; @Autowired private PasswordEncoder passwordEncoder; @Autowired private UserDetailsService userDetailsService; @Primary @Bean public InMemoryTokenStore inMemoryTokenStore () { return new InMemoryTokenStore(); } @Override public void configure (ClientDetailsServiceConfigurer clients) throws Exception { clients.inMemory() .withClient("clientId" ) .secret(passwordEncoder.encode("secret" )) .authorizedGrantTypes("authorization_code" , "password" , "refresh_token" , "client_credentials" ) .scopes("all" ) .autoApprove(true ) .redirectUris("http://localhost:8080" ); } @Override public void configure (AuthorizationServerEndpointsConfigurer endpoints) throws Exception { endpoints.tokenStore(inMemoryTokenStore()) .authenticationManager(authenticationManager) .reuseRefreshTokens(false ) .userDetailsService(userDetailsService); } @Override public void configure (AuthorizationServerSecurityConfigurer oauthServer) { oauthServer.allowFormAuthenticationForClients(); } }
安全配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private PasswordEncoder passwordEncoder; @Bean @ConditionalOnMissingBean(PasswordEncoder.class) public PasswordEncoder passwordEncoder () { return PasswordEncoderFactories.createDelegatingPasswordEncoder(); } @Bean @Override public AuthenticationManager authenticationManagerBean () throws Exception { return super .authenticationManagerBean(); } @Bean public UserDetailsService userDetailsService () { InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager(); manager.createUser(User.withUsername("admin" ).password(passwordEncoder.encode("admin" )).roles("ADMIN" ).build()); manager.createUser(User.withUsername("guest" ).password(passwordEncoder.encode("guest" )).roles("GUEST" ).build()); return manager; } @Override protected void configure (HttpSecurity http) throws Exception { http .authorizeRequests() .antMatchers("/oauth/**" ).permitAll() .anyRequest().authenticated() .and() .formLogin(); } }
非对称加密(这里没有使用)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 public class KeyConfig { private static final String KEY_STORE_FILE = ".keystore-oauth2-demo" ; private static final String KEY_STORE_PASSWORD = "admin1234" ; private static final String KEY_ALIAS = "oauth2-demo-key" ; private static KeyStoreKeyFactory KEY_STORE_KEY_FACTORY = new KeyStoreKeyFactory(new ClassPathResource(KEY_STORE_FILE), KEY_STORE_PASSWORD.toCharArray()); static final String VERIFIER_KEY_ID = Base64.getEncoder().encodeToString(KeyGenerators.secureRandom(32 ).generateKey()); static RSAPublicKey getVerifierKey () { return (RSAPublicKey) getKeyPair().getPublic(); } static RSAPrivateKey getSignerKey () { return (RSAPrivateKey) getKeyPair().getPrivate(); } private static KeyPair getKeyPair () { return KEY_STORE_KEY_FACTORY.getKeyPair(KEY_ALIAS); } }
测试
密码模式(password)
1 curl -X POST http://localhost:8080/oauth/token?grant_type=password&username=admin&password=admin&client_id=clientId&client_secret=secret
1 2 3 4 5 6 7 { "access_token" : "dba595be-6bbf-4c6b-8fd3-eac650f40c4e" , "token_type" : "bearer" , "refresh_token" : "916b2145-0e07-44d2-9a29-b0e6c02b399f" , "expires_in" : 43199 , "scope" : "all" }
1 curl http://localhost:8080/xxx/ping -H "Authorization: Bearer dba595be-6bbf-4c6b-8fd3-eac650f40c4e"
授权码模式(authorization-code)
1 http://localhost:8080/oauth/authorize?client_id=clientId&response_type=code&redirect_uri=http://localhost:8080&state=123
然后在登录表单填写帐号密码 admin/admin, 返回code和state
1 http://localhost:8080/?code=LHHPXA&state=123
1 curl -X POST http://localhost:8080/oauth/token?grant_type=authorization_code&code=LHHPXA&redirect_uri=http://localhost:8080&client_id=clientId&client_secret=secret
1 2 3 4 5 6 7 { "access_token" : "db2c779f-20f6-44df-98e1-5fc46a2fe9c5" , "token_type" : "bearer" , "refresh_token" : "c7f87939-6801-4269-b745-e306c94130c2" , "expires_in" : 43199 , "scope" : "all" }
1 curl http://localhost:8080/xxx/ping -H "Authorization: Bearer db2c779f-20f6-44df-98e1-5fc46a2fe9c5"
隐藏模式(implicit)
1 http://localhost:8080/oauth/authorize?response_type=token&scope=all&client_id=clientId&client_secret=secret&redirect_uri=http://localhost:8080&state=123
然后在登录表单填写帐号密码 admin/admin, 返回access_token和state
1 http://localhost:8080/#access_token=6b8c3fa7-0513-44e3-b7a1-ea89f927010d&token_type=bearer&state=123&expires_in=43167
1 curl http://localhost:8080/xxx/ping -H "Authorization: Bearer 6b8c3fa7-0513-44e3-b7a1-ea89f927010d"
客户端凭证(client credentials)
1 curl -X POST http://localhost:8080/oauth/token -H "Accept: application/json" -d "grant_type=client_credentials&scope=all&client_id=clientId&client_secret=secret"
1 2 3 4 5 6 { "access_token" : "a38c2319-30f3-45ef-ab6b-294111e8b207" , "token_type" : "bearer" , "expires_in" : 43199 , "scope" : "all" }
1 curl http://localhost:8080/xxx/ping -H "Authorization: Bearer a38c2319-30f3-45ef-ab6b-294111e8b20"
刷新token
以密码模式中的refresh_token演示
1 curl -X POST http://localhost:8080/oauth/token -H "Accept: application/json" -d "grant_type=refresh_token&refresh_token=916b2145-0e07-44d2-9a29-b0e6c02b399f&client_id=clientId&client_secret=secret"
1 2 3 4 5 6 7 { "access_token" : "fcc85ce0-426d-421f-b2dc-005ebad3f9da" , "token_type" : "bearer" , "refresh_token" : "54880f4c-b5fe-4163-82c3-ae2e9fd90b85" , "expires_in" : 43199 , "scope" : "all" }
1 curl http://localhost:8080/xxx/ping -H "Authorization: Bearer fcc85ce0-426d-421f-b2dc-005ebad3f9da"
最后
本文到此结束,感谢阅读。如果您觉得不错,请关注公众号【当我遇上你】,您的支持是我写作的最大动力。