Spring Security Oauth2 示例

所有示例的依赖如下(均是SpringBoot项目)

pom.xml

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </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>
    </dependency>
    <dependency>
        <groupId>org.springframework.security</groupId>
        <artifactId>spring-security-jwt</artifactId>
    </dependency>
</dependencies>

一、ResourceServer(资源服务器)

application.yml

server:
  port: 8082
  context-path: /resourceserver

启动类

@SpringBootApplication
public class ResourceServerApplication {

    public static void main(String[] args) {
        SpringApplication.run(ResourceServerApplication.class, args);
    }
}

TestController.java(暴露RESTful接口)

@RestController
public class TestController {

    @GetMapping("/product/{id}")
    public String product(@PathVariable String id) {
        return "product id : " + id;
    }

    @GetMapping("/order/{id}")
    public String order(@PathVariable String id) {
        return "order id : " + id;
    }

    @GetMapping("/pomer/{id}")
    public String pomer(@PathVariable String id) {
        return "pomer id : " + id;
    }
}

配置类 ResourceServerConfig(配置JWT形式的token)

@EnableResourceServer
@Configuration
public class ResourceServerConfig {

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

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

配置类 MyResourceServerConfigurer(配置访问权限)

@Configuration
public class MyResourceServerConfigurer extends ResourceServerConfigurerAdapter {

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                .antMatchers("/product/**").permitAll()
                .antMatchers("/order/**").authenticated()
                .antMatchers("/pomer/**").access("#oauth2.hasScope('read_profile') and hasAuthority('ADMIN')");
    }
}

二、AuthorizationServer 授权服务器(授权码模式,authorization code)

application.yml

server:
  port: 8081
  context-path: /authorizationserver

启动类

@SpringBootApplication
public class AuthorizationServerApplication {

    public static void main(String[] args) {
        SpringApplication.run(AuthorizationServerApplication.class, args);
    }
}

配置类 AuthorizationServerConfig(配置JWT形式的token)

@EnableAuthorizationServer
@Configuration
public class AuthorizationServerConfig {

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

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

配置类 MyAuthorizationServerConfigurer

@Configuration
public class MyAuthorizationServerConfigurer extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private AuthenticationManager authenticationManager;
    @Autowired
    private TokenStore tokenStore;
    @Autowired
    private JwtAccessTokenConverter jwtAccessTokenConverter;

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints.tokenStore(tokenStore)
                .accessTokenConverter(jwtAccessTokenConverter)
                .authenticationManager(authenticationManager);
    }

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
                .withClient("clientApp")
                .secret("123456")
                .redirectUris("http://localhost:9000/callback")
                .authorizedGrantTypes("authorization_code")
                .scopes("read_profile", "read_contacts");
    }
}

配置类 MyWebSecurityConfigurerAdapter(配置用户名密码)

@Configuration
public class MyWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
                .withUser("user").password("123456").authorities("USER").and()
                .withUser("admin").password("123456").authorities("USER", "ADMIN");
    }
}

开始测试!打开浏览器访问:

http://localhost:8081/authorizationserver/oauth/authorize?client_id=clientApp&redirect_uri=http://localhost:9000/callback&response_type=code&scope=read_profile

显示登录页面,输入用户名密码,返回重定向302(授权码位于参数部分)

http://localhost:9000/callback?code=EWfpi5

根据授权码,请求令牌:

>  curl -X POST --user clientApp:123456 http://localhost:8081/authorizationserver/oauth/token -H "Content-Type: application/x-www-form-urlencoded" -d "grant_type=authorization_code&redirect_uri=http://localhost:9000/callback&scope=read_profile&code=EWfpi5"

# clientApp:123456 经Base64编码得 'Y2xpZW50QXBwOjEyMzQ1Ng=='
POST /authorizationserver/oauth/token HTTP/1.1
Host: localhost:8081
Content-Type: application/x-www-form-urlencoded
Authorization: Basic Y2xpZW50QXBwOjEyMzQ1Ng==
Cache-Control: no-cache
Postman-Token: c6c182c7-5df1-46ea-9cb7-130ff250c93c

code=F4QesT&grant_type=authorization_code&redirect_uri=http%3A%2F%2Flocalhost%3A9000%2Fcallback&scope=read_profile

得到令牌:

{
"access_token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NDE4NTExMTYsInVzZXJfbmFtZSI6InVzZXIiLCJhdXRob3JpdGllcyI6WyJVU0VSIl0sImp0aSI6ImMyYzQ4YTc0LWI4MjUtNGNlYS05NGU3LTMyM2VlN2Q4NTk2ZCIsImNsaWVudF9pZCI6ImNsaWVudEFwcCIsInNjb3BlIjpbInJlYWRfcHJvZmlsZSJdfQ.Oc1TN7fMPgNVXX7k8dEw0QqosD7ZKQ198c-Qa2l1Gho",
"token_type":"bearer",
"expires_in":43199,
"scope":"read_profile",
"jti":"c2c48a74-b825-4cea-94e7-323ee7d8596d"
}

根据令牌请求资源

> curl http://localhost:8082/resourceserver/order/12 -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NDE4NTExMTYsInVzZXJfbmFtZSI6InVzZXIiLCJhdXRob3JpdGllcyI6WyJVU0VSIl0sImp0aSI6ImMyYzQ4YTc0LWI4MjUtNGNlYS05NGU3LTMyM2VlN2Q4NTk2ZCIsImNsaWVudF9pZCI6ImNsaWVudEFwcCIsInNjb3BlIjpbInJlYWRfcHJvZmlsZSJdfQ.Oc1TN7fMPgNVXX7k8dEw0QqosD7ZKQ198c-Qa2l1Gho"

GET /resourceserver/order/12 HTTP/1.1
Host: localhost:8082
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NDE4NTExMTYsInVzZXJfbmFtZSI6InVzZXIiLCJhdXRob3JpdGllcyI6WyJVU0VSIl0sImp0aSI6ImMyYzQ4YTc0LWI4MjUtNGNlYS05NGU3LTMyM2VlN2Q4NTk2ZCIsImNsaWVudF9pZCI6ImNsaWVudEFwcCIsInNjb3BlIjpbInJlYWRfcHJvZmlsZSJdfQ.Oc1TN7fMPgNVXX7k8dEw0QqosD7ZKQ198c-Qa2l1Gho
Cache-Control: no-cache
Postman-Token: 913a8844-1308-47d8-afa3-a9f288323c20

返回资源

order id : 12

附:/product/* 允许任何人访问,/order/* 需要用户认证,而 /pomer/* 只允许拥有ADMIN权限的admin用户访问,测试有效

三、AuthorizationServer 授权服务器(简化模式,implicit)

只修改配置类 MyAuthorizationServerConfigurer,其余代码不变

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
                .withClient("clientApp")
                .secret("123456")
                .redirectUris("http://localhost:9000/callback")
                .authorizedGrantTypes("implicit")
                .scopes("read_profile","read_contacts");
    }

开始测试!打开浏览器访问:

http://localhost:8081/authorizationserver/oauth/authorize?client_id=clientApp&redirect_uri=http://localhost:9000/callback&response_type=token&scope=read_profile&state=xyz

显示登录页面,输入用户名密码,返回重定向302(令牌位于hash部分):

http://localhost:9000/callback#access_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NDE4NjMxNjMsInVzZXJfbmFtZSI6InVzZXIiLCJhdXRob3JpdGllcyI6WyJVU0VSIl0sImp0aSI6ImQ2MzYxYzZjLTIyZWYtNDU2ZC1iOGJhLWY4ZTE5MzMwMTBmOSIsImNsaWVudF9pZCI6ImNsaWVudEFwcCIsInNjb3BlIjpbInJlYWRfcHJvZmlsZSJdfQ.BRwtzF2rFc6w8WECqYETZbkCSDdoOzKdF4Zm_SAZuao&token_type=bearer&state=xyz&expires_in=119&jti=d6361c6c-22ef-456d-b8ba-f8e1933010f9

得到令牌,后续请求资源相同,不再重复叙述

四、AuthorizationServer 授权服务器(密码模式,resource owner password credentials)

修改配置类 MyAuthorizationServerConfigurer

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
                .withClient("clientApp")
                .secret("123456")
                .authorizedGrantTypes("password")
                .scopes("read_profile", "read_contacts");
    }

在默认情况下 AuthorizationServerEndpointsConfigurer 配置没有开启密码模式,只有配置了 authenticationManager 才会开启密码模式,如下

在 MyWebSecurityConfigurerAdapter.java 新增一段代码(配置 authenticationManager Bean)

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

在 MyAuthorizationServerConfigurer.java 新增一段代码(令 AuthorizationServerEndpointsConfigurer 设置 authenticationManager)

    @Autowired
    private AuthenticationManager authenticationManager;
    @Autowired
    private TokenStore tokenStore;
    @Autowired
    private JwtAccessTokenConverter jwtAccessTokenConverter;

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
        endpoints.tokenStore(tokenStore)
                .accessTokenConverter(jwtAccessTokenConverter)
                .authenticationManager(authenticationManager);
    }

开始测试!请求令牌

>  curl -X POST --user clientApp:123456 http://localhost:8081/authorizationserver/oauth/token -H "Content-Type: application/x-www-form-urlencoded" -d "grant_type=password&scope=read_profile&username=user&password=123456"

得到令牌:

{
"access_token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE1NDE5MDk4MTcsInVzZXJfbmFtZSI6InVzZXIiLCJhdXRob3JpdGllcyI6WyJVU0VSIl0sImp0aSI6ImJhZDk1OTI0LTdkYTgtNDUyMy05YzZkLTBkNzY3NTlmYjQ1NiIsImNsaWVudF9pZCI6ImNsaWVudEFwcCIsInNjb3BlIjpbInJlYWRfcHJvZmlsZSJdfQ.gO5_kl8_OBRnj2Dt5glflXAIJrbyioYXezV-ZQ8BxL4",
"token_type":"bearer",
"expires_in":43199,
"scope":"read_profile",
"jti":"bad95924-7da8-4523-9c6d-0d76759fb456"
}

得到令牌,后续请求资源相同,不再重复叙述

五、AuthorizationServer 授权服务器(客户端模式,client credentials)

只修改配置类 MyAuthorizationServerConfigurer,其余代码不变

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
                .withClient("clientApp")
                .secret("123456")
                .authorizedGrantTypes("client_credentials")
                .scopes("read_profile", "read_contacts");
    }

开始测试!请求令牌

>  curl -X POST --user clientApp:123456 http://localhost:8081/authorizationserver/oauth/token -H "Content-Type: application/x-www-form-urlencoded" -d "grant_type=client_credentials&scope=read_profile"

得到令牌:

{
"access_token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzY29wZSI6WyJyZWFkX3Byb2ZpbGUiXSwiZXhwIjoxNTQxOTEwOTEzLCJqdGkiOiI5MDVjNzc2OS1lNGQ2LTQxYTItYjcyMC1jYzliZWZjYzE5MDQiLCJjbGllbnRfaWQiOiJjbGllbnRBcHAifQ.c6UiHxbCdioCklkCqkLsCG6C8KHwwzajlPka6ut5MJs",
"token_type":"bearer",
"expires_in":43199,
"scope":"read_profile",
"jti":"905c7769-e4d6-41a2-b720-cc9befcc1904"
}

得到令牌,后续请求资源相同,不再重复叙述

猜你喜欢

转载自www.cnblogs.com/pomer-huang/p/pomer_huang.html