客户端信息入库

oauth_client_details

TIP

  • 为什么要入库 ??
    • 配置资源服务器需要为每一个App配置一个AppID
    • 优雅
    • 简单
    • 等等.........

之前的写法

  • (这个还是只配置了一个)
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
  clients.inMemory()
      .withClient("javaboy")
      .secret(new BCryptPasswordEncoder().encode("123"))
      .resourceIds("res1")
      .authorizedGrantTypes("authorization_code","refresh_token")
      .scopes("all")
      .redirectUris("http://localhost:8082/index.html");
}
  • 客户端信息入库涉及到的接口主要是 ClientDetailsService,这个接口主要有两个实现类
    • InMemoryClientDetailsService
    • JdbcClientDetailsService
DROP TABLE IF EXISTS `oauth_client_details`;
CREATE TABLE `oauth_client_details` (
  `client_id` varchar(48) NOT NULL,
  `resource_ids` varchar(256) DEFAULT NULL,
  `client_secret` varchar(256) DEFAULT NULL,
  `scope` varchar(256) DEFAULT NULL,
  `authorized_grant_types` varchar(256) DEFAULT NULL,
  `web_server_redirect_uri` varchar(256) DEFAULT NULL,
  `authorities` varchar(256) DEFAULT NULL,
  `access_token_validity` int(11) DEFAULT NULL,
  `refresh_token_validity` int(11) DEFAULT NULL,
  `additional_information` varchar(4096) DEFAULT NULL,
  `autoapprove` varchar(256) DEFAULT NULL,
  PRIMARY KEY (`client_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
  • auth-server 添加数据库依赖,和配置 application.properties
spring.datasource.url=jdbc:mysql:///ssm?useUnicode=true&characterEncoding=UTF-8&serverTimeZone=Asia/Shanghai
spring.datasource.password=0
spring.datasource.username=root

spring.main.allow-bean-definition-overriding=true

这里的配置多了最后一条。这是因为我们一会要创建自己的 ClientDetailsService,而系统已经创建了 ClientDetailsService,加了最后一条就允许我们自己的实例覆盖系统默认的实例。

  • 接下来,我们来提供自己的实例即可:
@Autowired
DataSource dataSource;
@Bean
ClientDetailsService clientDetailsService() {
    return new JdbcClientDetailsService(dataSource);
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
    clients.withClientDetails(clientDetailsService());
}
  • 配置完成后,重启 auth-server,走一遍第三方登录流程

  • 修改后的 AuthorizationServerTokenServices 实例如下:

@Bean
AuthorizationServerTokenServices tokenServices() {
    DefaultTokenServices services = new DefaultTokenServices();
    services.setClientDetailsService(clientDetailsService());
    services.setSupportRefreshToken(true);
    services.setTokenStore(tokenStore);
    return services;
}

授权 URL{id=authorized-url}

基于 HttpSecurity 配置 {id=http-security}

 
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;
 
@Configuration
public class ResourceServerConfig {

    @Bean
    public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {

        http.authorizeRequests(customizer -> {

            // 这里可以根据 下方表格 配置
            // 这里可以根据 下方表格 配置
            // 这里可以根据 下方表格 配置

            // 其他地址:需要授权访问
            // 如果需要此配置,必须要放在最后一行,否则启动项目报错
            customizer.anyRequest().authenticated();

        });

        return http.build();
    }

}
  1. 注意:POSTPUTPATCHDELETE 可能会被 CSRF 拦截而响应 401, 请参考:CSRF 跨站请求伪造
  2. mvcMatchers 基于 org.springframework.security.web.servlet.util.matcher.MvcRequestMatcher 实现,方法支持正则表达式,可以填写多个路径
  3. regexMatchers 基于 org.springframework.security.web.util.matcher.RegexRequestMatcher 实现,方法支持正则表达式,可以填写多个路径
  4. antMatchers 基于 org.springframework.security.web.util.matcher.AntPathRequestMatcher 实现,方法支持正则表达式,可以填写多个路径
  5. requestMatchers 无论是使用字符串还是接口 RequestMatcher 的实现,都支持填写多个值(参数为数组)
    1. Spring Boot 2 不支持使用字符串,仅支持使用 RequestMatcher 的实现
    2. Spring Boot 3 支持使用 字符串 和 RequestMatcher 的实现
使用方法:作用
customizer.mvcMatchers("/a1").permitAll();/a1 路径允许所有人访问,基于 MvcRequestMatcher 实现
customizer.mvcMatchers(HttpMethod.GET, "/a2").permitAll();/a2 仅允许使用 GET 请求 匿名访问
customizer.mvcMatchers("/a3").hasAuthority("A1");/a3 拥有 A1 权限的用户才能访问
customizer.mvcMatchers("/a4").hasAnyAuthority("A1", "A2");/a4 拥有 A1 A2 权限的用户才能访问
customizer.mvcMatchers("/a5").hasRole("R1");/a5 拥有 R1 角色的用户才能访问
customizer.mvcMatchers("/a6").hasAnyRole("R1", "R2");/a6 拥有 R1 R2 角色的用户才能访问
customizer.mvcMatchers("/a7").hasIpAddress("192.168.0.0/16");/a7192.168.0.0/16 发送请求可以匿名访问
customizer.mvcMatchers("/a8").rememberMe();/a8 使用 记住我 功能登陆的用户可以访问
customizer.mvcMatchers("/a9").fullyAuthenticated();/a9 必须完整验证身份后才能访问,使用 记住我 功能登陆的用户无法访问
customizer.mvcMatchers("/a10").denyAll();/a10 访问
customizer.mvcMatchers("/a11").not().hasAuthority("A1");/a11 取反,拥有 A1 权限才能访问
customizer.mvcMatchers("/a12").access("hasAuthority('A1')");/a12 与上方 /a3 结果一致
customizer.mvcMatchers("/a13").access("hasAuthority('A1') and hasAuthority('A2')");/a13 同时拥有 A1 A2 权限才能访问
customizer.mvcMatchers("/a14").access("!hasAuthority('A1')");/a14 取反,拥有 A1 权限才能访问
customizer.regexMatchers("/a21").permitAll();/a21mvcMatchers 方法雷同,使用 RegexRequestMatcher 实现
customizer.antMatchers("/a31").permitAll();/a31mvcMatchers 方法雷同,使用 AntPathRequestMatcher 实现
使用方法:作用
customizer.requestMatchers("/a1").permitAll();/a1 路径允许所有人访问,基于 MvcRequestMatcher 实现
customizer.requestMatchers(HttpMethod.GET, "/a2").permitAll();/a2 仅允许使用 GET 请求 匿名访问
customizer.requestMatchers("/a3").hasAuthority("A1");/a3 拥有 A1 权限的用户才能访问
customizer.requestMatchers("/a4").hasAnyAuthority("A1", "A2");/a4 拥有 A1 A2 权限的用户才能访问
customizer.requestMatchers("/a5").hasRole("R1");/a5 拥有 R1 角色的用户才能访问
customizer.requestMatchers("/a6").hasAnyRole("R1", "R2");/a6 拥有 R1 R2 角色的用户才能访问
customizer.requestMatchers("/a8").rememberMe();/a8 使用 记住我 功能登陆的用户可以访问
customizer.requestMatchers("/a9").fullyAuthenticated();/a9 必须完整验证身份后才能访问,使用 记住我 功能登陆的用户无法访问
customizer.requestMatchers("/a10").denyAll();/a10 访问
使用方法:作用
customizer.requestMatchers(request -> { /* 这里可以使用 request 进行判断*/ return true;}).permitAll();可以根据 HttpServletRequest 进行自定义判断,最后的 permitAll 也可修改为 hasRole 等等

基于 @PreAuthorize 配置 {id=pre-authorize}

参见:使用 @PreAuthorize 注解