1. 工程结构
1.1项目代码
pom文件设置
<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 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>cn.huangwei</groupId> <artifactId>oauth-demo</artifactId> <version>0.0.1-SNAPSHOT</version>
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.7.RELEASE</version> <relativePath /> <!-- lookup parent from repository --> </parent>
<dependencies> <!-- 注意是starter,自动配置 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <!-- 不是starter,手动配置 --> <dependency> <groupId>org.springframework.security.oauth</groupId> <artifactId>spring-security-oauth2</artifactId> <version>2.3.5.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
</dependencies>
</project> |
application.yml文件设置
server: port: 10011 |
OauthApp.java文件设置
package cn.huangwei.app;
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication public class OauthApp { public static void main(String[] args) { SpringApplication.run(OauthApp.class, args); } } |
Test EndPoint.java 文件配置
package cn.huangwei.app;
import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RestController;
@RestController public class TestEndpoints {
@GetMapping("/product/{id}") public String getProduct(@PathVariable String id) { //for debug Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); return "product id : " + id; }
@GetMapping("/order/{id}") public String getOrder(@PathVariable String id) { //for debug Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); return "order id : " + id; }
}
|
SecurityConfiguration.java文件设置
package cn.huangwei.app;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; 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.factory.PasswordEncoderFactories; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.provisioning.InMemoryUserDetailsManager;
@Configuration @EnableWebSecurity public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
@Bean @Override protected UserDetailsService userDetailsService() { BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder(); // password 方案三:支持多种编码,通过密码的前缀区分编码方式 String finalPassword = "{bcrypt}" + bCryptPasswordEncoder.encode("123456"); InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager(); manager.createUser(User.withUsername("user_1").password(finalPassword).authorities("USER").build()); manager.createUser(User.withUsername("user_2").password(finalPassword).authorities("USER").build()); return manager; }
// password 方案三:支持多种编码,通过密码的前缀区分编码方式,推荐 @Bean PasswordEncoder passwordEncoder() { return PasswordEncoderFactories.createDelegatingPasswordEncoder(); }
/** * 这一步的配置是必不可少的,否则SpringBoot会自动配置一个AuthenticationManager,覆盖掉内存中的用户 */ @Bean @Override public AuthenticationManager authenticationManagerBean() throws Exception { AuthenticationManager manager = super.authenticationManagerBean(); return manager; }
@Override protected void configure(HttpSecurity http) throws Exception { http.requestMatchers().anyRequest().and().authorizeRequests().antMatchers("/oauth/*").permitAll(); } }
|
Oauth2ServerConfig.java文件设置
package cn.huangwei.app;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpMethod; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.http.SessionCreationPolicy; 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.configuration.EnableResourceServer; import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter; 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.config.annotation.web.configurers.ResourceServerSecurityConfigurer; import org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore;
@Configuration public class OAuth2ServerConfig {
private static final String DEMO_RESOURCE_ID = "order";
@Configuration @EnableResourceServer protected static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
@Override public void configure(ResourceServerSecurityConfigurer resources) { resources.resourceId(DEMO_RESOURCE_ID).stateless(true); }
@Override public void configure(HttpSecurity http) throws Exception { http .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED) .and() .requestMatchers().anyRequest() .and() .anonymous() .and() .authorizeRequests() .antMatchers("/order/**").authenticated();//配置order访问控制,必须认证过后才可以访问 } }
@Configuration @EnableAuthorizationServer protected static class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
@Autowired AuthenticationManager authenticationManager;
@Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { //配置两个客户端,一个用于password认证一个用于client认证 String finalSecret = "{bcrypt}"+new BCryptPasswordEncoder().encode("123456"); //配置两个客户端,一个用于password认证一个用于client认证 clients.inMemory().withClient("client_1") .resourceIds(DEMO_RESOURCE_ID) .authorizedGrantTypes("client_credentials", "refresh_token") .scopes("select") .authorities("oauth2") .secret(finalSecret) .and().withClient("client_2") .resourceIds(DEMO_RESOURCE_ID) .authorizedGrantTypes("password", "refresh_token") .scopes("select") .authorities("oauth2") .secret(finalSecret); }
@Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { endpoints .tokenStore(new InMemoryTokenStore()) .authenticationManager(authenticationManager) .allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST); }
@Override public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception { //允许表单认证 oauthServer.allowFormAuthenticationForClients(); }
}
}
|
运行OauthApp工程文件,然后浏览器中输入client模式:
http://localhost:8080/oauth/token?grant_type=client_credentials&scope=select&client_id=client_1&client_secret=123456
响应如下:
{"access_token":"fbd5a92b-39b0-4330-b195-bd5518539f41","token_type":"bearer","expires_in":42102,"scope":"select"}
1.2各个java 文件的作用:
资源服务器OAuth2ServerConfig-ResourceServerConfiguration
使用@EnableResourceServer修饰的Configurer接口。实现此接口以调整受OAuth2安全性保护的访问规则和路径。应用程序可以提供此接口的多个实例,如果多个实例配置相同的属性,则使用最后一个配置的属性值。在应用之前,配置程序按{@link Order}排序。
该接口有两个方法
① 添加资源的相关属性
② 配置资源的安全访问规则,默认是所有在/oauth/**的资源都应该被保护
一般去继承ResourceServerConfigurerAdapter这个接口类。然后实现相关方法。
@EnableResourceServer注解自动增加了一个类型为OAuth2AuthenticationProcessin-gFilter的过滤器链
认证服务器OAuth2ServerConfig-AuthorizationServerConfiguration
使用@EnableAuthorizationServer来修饰一个类,表明认证服务器配置类。
有三个方法:
①配置客户端的相关信息,id,secret,scope等信息
②配置认证endPoint的相关信息,tokenstore类型,authenticationManager,允许生成token的request method,例如get,most等。
③配置授权服务器/oauth/token endPoint的安全性,一般采用默认规则,即使不配置也能使用,创建ClientCredentialsTokenEndpointFilter核心过滤器
TestEndpoints为controller用于验证安全访问的请求,不再赘述
SecurityConfiguration文件
配置用户的信息,以及一些需要在认证服务器中使用的bean实例,config(HttpSecurity http)用户设置哪些方法能够通行,不需要经过安全认证