Spring Security 自定义授权服务器实践记录

Spring Security 自定义授权服务器实践记录

本文将详细讲解如何使用Spring Security自定义授权服务器,并提供两个示例说明。

前置条件

在开始学习本文前,需要准备以下环境:

  • JDK1.8或以上版本
  • Maven 3.0或以上版本
  • Spring Boot 2.0或以上版本

配置依赖

首先,需要在pom.xml中添加以下依赖:

<dependencies>
    <dependency>
        <groupId>org.springframework.security.oauth.boot</groupId>
        <artifactId>spring-security-oauth2-autoconfigure</artifactId>
        <version>2.1.0.RELEASE</version>
    </dependency>
</dependencies>

配置授权服务器

接下来,需要在应用程序中配置授权服务器。可以通过继承AuthorizationServerConfigurerAdapter类来配置授权服务器。在这里,需要重写几个方法:

  • configure(ClientDetailsServiceConfigurer clients) - 用于配置客户端详情信息;
  • configure(AuthorizationServerEndpointsConfigurer endpoints) - 用于配置授权(authorization)和令牌(token)的访问端点和令牌服务(token services);
  • configure(AuthorizationServerSecurityConfigurer security) - 用于配置令牌端点(token endpoint)的安全约束,即哪些请求将会被保护起来。

下面是一个示例:

@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private UserDetailsService userDetailsService;

    @Autowired
    private TokenStore tokenStore;

    @Autowired
    private JwtAccessTokenConverter jwtAccessTokenConverter;

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
                .withClient("client_id")
                .secret("client_secret")
                .scopes("read", "write")
                .authorizedGrantTypes("password", "refresh_token")
                .accessTokenValiditySeconds(3600)
                .refreshTokenValiditySeconds(86400*7);
    }

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

    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
        security.tokenKeyAccess("permitAll()")
                .checkTokenAccess("isAuthenticated()")
                .allowFormAuthenticationForClients();
    }
}

配置资源服务器

接下来需要配置资源服务器。在应用程序中,可以使用@EnableResourceServer注解来开启资源服务器,并实现ResourceServerConfigurer接口来配置资源服务器。

下面是一个示例:

@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests().antMatchers("/api/**").authenticated();
    }

    @Override
    public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
        resources.resourceId("resource_id").stateless(true);
    }
}

在这里,configure(HttpSecurity http)方法会配置请求的授权规则,configure(ResourceServerSecurityConfigurer resources)方法会配置资源服务器的ID。

示例1:密码模式

在密码模式中,用户需要在登录页面输入用户名和密码。应用程序会通过OAuth2协议向授权服务器发起请求,以获取访问令牌(access token)。在使用访问令牌访问资源时,资源服务器会验证令牌的有效性,并返回相应的资源。

配置用户和角色

首先,需要配置一个用户和角色:

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsService userDetailsService;

    @Bean
    public BCryptPasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

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

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
                .authorizeRequests().anyRequest().authenticated()
                .and().formLogin().loginPage("/login").permitAll()
                .and().logout().permitAll();
    }

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

在这里,通过@Bean注解,配置了一个密码编码器(passwordEncoder()方法)和一个身份验证管理器(authenticationManagerBean()方法)。同时,在configure(AuthenticationManagerBuilder auth)方法中,将userDetailsService注入到AuthenticationManagerBuilder中,以便在身份验证过程中使用。

请求认证

接下来,在应用程序中,需要创建一个控制器来接收用户名和密码并向授权服务器请求访问令牌。这里,需要使用AuthorizationCodeAccessTokenProvider来进行请求。

@RestController
public class AuthController {

    @Autowired
    private AuthorizationServerTokenServices tokenServices;

    @Autowired
    private ClientDetailsService clientDetailsService;

    @RequestMapping("/oauth/token")
    public OAuth2AccessToken getToken(
            @RequestParam Map<String, String> parameters,
            HttpServletRequest request) throws ServletException {
        String grantType = parameters.get("grant_type");
        String clientId = parameters.get("client_id");
        ClientDetails clientDetails = clientDetailsService.loadClientByClientId(clientId);
        TokenRequest tokenRequest = new TokenRequest(parameters, clientId, clientDetails.getScope(),
                grantType);
        OAuth2Request oAuth2Request = tokenRequest.createOAuth2Request(clientDetails);
        OAuth2Authentication oAuth2Authentication = new OAuth2Authentication(oAuth2Request,
                null);
        OAuth2AccessToken token = tokenServices.createAccessToken(oAuth2Authentication);
        return token;
    }
}

在这里,通过@Autowired注解,注入了AuthorizationServerTokenServicesClientDetailsService。在getToken方法中,通过parameters中的grant_typeclient_id来获取clientDetailstokenRequest。然后,使用AuthorizationServerTokenServices来创建一个访问令牌(OAuth2AccessToken)。

发布资源

最后,发布一个资源,以便访问。在这里,发布了一个API,可以在使用访问令牌后访问。

@RestController
@RequestMapping("/api")
public class ApiController {

    @GetMapping("/hello")
    public String hello() {
        return "Hello World!";
    }
}

测试认证

启动应用程序,并访问http://localhost:8080/login,输入用户名和密码来进行身份验证。然后,使用下面的curl命令来向授权服务器请求访问令牌:

curl -X POST -u client_id:client_secret "http://localhost:8080/oauth/token?grant_type=password&username=user&password=password"

如果请求成功,将会返回如下JSON格式的响应:

{
    "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9",
    "token_type": "bearer",
    "expires_in": 3599,
    "refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9",
    "scope": [
        "read",
        "write"
    ]
}

使用获取到的访问令牌,通过下面的curl命令来访问API:

curl -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9" "http://localhost:8080/api/hello"

如果请求成功,将会返回如下响应:

Hello World!

示例2:客户端模式

在客户端模式中,客户端向授权服务器发起请求,以获取访问令牌。在使用访问令牌访问资源时,资源服务器会验证令牌的有效性,并返回相应的资源。

配置客户端

首先,需要在授权服务器中配置客户端,并为其分配client_idclient_secret。下面是一个示例:

@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
                .withClient("client_id")
                .secret("client_secret")
                .scopes("read", "write")
                .authorizedGrantTypes("client_credentials")
                .accessTokenValiditySeconds(3600);
    }

    //其他方法
}

在这里,通过clients.inMemory()方法创建一个InMemoryClientDetailsService实例,并为其添加一个名为client_id的客户端。该客户端使用client_credentials授权方式,并且授权范围为readwrite

请求认证

然后,需要编写一个发送鉴权请求的客户端。下面是一个示例:

@Component
public class AuthClient {

    private RestTemplate restTemplate;

    private String url = "http://localhost:8080/oauth/token";

    @Value("${client.id}")
    private String clientId;

    @Value("${client.secret}")
    private String clientSecret;

    public AuthClient(RestTemplate restTemplate) {
        this.restTemplate = restTemplate;
    }

    public String getAccessToken() {
        HttpHeaders headers = new HttpHeaders();
        String auth = clientId + ":" + clientSecret;
        byte[] encodedAuth = Base64.encodeBase64(
          auth.getBytes(Charset.forName("US-ASCII")));
        String authHeader = "Basic " + new String(encodedAuth);
        headers.set("Authorization", authHeader);
        MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
        map.add("grant_type", "client_credentials");
        HttpEntity<MultiValueMap<String, String>> entity = new 
          HttpEntity<>(map, headers);
        ResponseEntity<AccessToken> response = restTemplate.exchange(url, HttpMethod.POST, entity, 
          AccessToken.class);
        return response.getBody().getAccessToken();
    }
}

在这里,通过@Value注解,注入了client_idclient_secret。在getAccessToken方法中,创建一个包含请求头和请求体的HttpEntity对象,并使用此对象向授权服务器发出请求,以获取访问令牌。

发布资源

最后,在资源服务器中发布一个API,以便访问。同时,在该API中使用@PreAuthorize注解来添加安全性。下面是一个示例:

@RestController
@RequestMapping("/api")
public class ApiController {

    @PreAuthorize("hasAuthority('read')")
    @GetMapping("/hello")
    public String hello() {
        return "Hello World!";
    }
}

在这里,使用@PreAuthorize注解,添加了一个安全性规则,只有拥有read权限的用户才能访问该API。

测试认证

启动应用程序,然后通过下面的curl命令来向授权服务器请求访问令牌:

curl -X POST -u client_id:client_secret "http://localhost:8080/oauth/token?grant_type=client_credentials"

如果请求成功,将会返回如下JSON格式的响应:

{
    "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9",
    "token_type": "bearer",
    "expires_in": 3599,
    "scope": [
        "read",
        "write"
    ]
}

使用获取到的访问令牌,通过下面的curl命令来访问API:

curl -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9" "http://localhost:8080/api/hello"

如果请求成功,将会返回如下响应:

Hello World!

结论

本文详细讲解了如何使用Spring Security自定义授权服务器,包括配置授权服务器和资源服务器,并提供了两个示例来说明如何使用密码模式和客户端模式进行认证。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Spring Security 自定义授权服务器实践记录 - Python技术站

(0)
上一篇 2023年6月3日
下一篇 2023年6月3日

相关文章

  • boot-admin整合flowable官方editor-app源码进行BPMN2-0建模(续)

    boot-admin整合flowable官方editor-app源码进行BPMN2-0建模(续)书接上回 项目源码仓库github项目源码仓库gitee boot-admin 是一款采用前后端分离模式、基于SpringCloud微服务架构的SaaS后台管理框架。系统内置基础管理、权限管理、运行管理、定义管理、代码生成器和办公管理6个功能模块,集成分布式事务S…

    Java 2023年4月22日
    00
  • 基于jdk1.8的Java源码详解 Integer

    基于 JDK1.8 的 Java 源码详解 Integer 介绍 本文将会详细讲解 JDK1.8 版本中的 Integer 类的源码实现。Integer 类是 Java 中表示整数类型的包装类,在日常开发中非常常用。通过对其源码的分析和理解,可以帮助程序员更好的理解 Java 中整数类型的实现方式,有助于优化代码和解决实际问题。 Integer 类的源码结构…

    Java 2023年5月23日
    00
  • SpringBoot2整合Drools规则引擎及案例详解

    Spring Boot 2整合Drools规则引擎及案例详解可以分为以下几个步骤: 第一步:引入Drools依赖 在pom.xml文件中引入Drools的依赖: <dependency> <groupId>org.drools</groupId> <artifactId>drools-core</arti…

    Java 2023年5月19日
    00
  • 在React 组件中使用Echarts的示例代码

    使用Echarts在React组件中展示图表是很常见的需求。下面是一个完整的示例代码,你可以根据你自己的需求进行修改和调整。 安装 Echarts 首先,我们需要安装 Echarts。 使用 npm 安装 bash npm install echarts –save 使用 yarn 安装 bash yarn add echarts 导入 Echarts 在…

    Java 2023年6月15日
    00
  • java打印正弦曲线示例

    下面我会详细讲解Java打印正弦曲线示例的完整攻略,请耐心阅读。 Java打印正弦曲线示例 简介 本文将介绍使用Java打印正弦曲线的过程,并附上代码示例和详细说明。正弦曲线是一种常见的数学曲线,它可以通过一系列的正弦函数值计算得出并绘制出曲线。 准备工作 在开始正弦曲线的绘制之前,需要先准备好Java开发环境。安装好JDK并配置好环境变量后,打开编辑器开始…

    Java 2023年5月26日
    00
  • Java根据模板导出Excel报表并复制模板生成多个Sheet页

    讲解”Java根据模板导出Excel报表并复制模板生成多个Sheet页”的攻略,具体步骤如下: 步骤一:引入依赖 首先需要引入以下依赖: <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <v…

    Java 2023年5月20日
    00
  • Jsp自定义标签和方法详解

    下面我来详细讲解“Jsp自定义标签和方法详解”的完整攻略。 一、自定义标签 1.1 概述 JSP标签可以分为三类:JSTL标签、自定义标签和自定义函数。其中,自定义标签是指在JSP页面中使用自己开发的标签,实现特定的功能。 1.2 步骤 自定义标签的开发主要分为以下步骤: 1)创建TLD文件:在Web应用的WEB-INF目录下创建一个.tld文件,用于描述标…

    Java 2023年6月15日
    00
  • 如何在java中使用Jython

    使用Jython,可以在Java的运行环境下直接执行Python代码,将Python和Java的优点融合到一起。以下是在Java中使用Jython的完整攻略: 1. 下载Jython 在官网 https://www.jython.org/download 中下载Jython最新稳定版本的zip文件。解压后可以得到一个jython.jar文件,这个文件就是我们…

    Java 2023年5月19日
    00
合作推广
合作推广
分享本页
返回顶部