spring security和spring oauth结合后 spring security配置不管用了
    spring security
@Configuration
@EnableResourceServer
public class AppWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {

    @Autowired
    private ZuulProperties zuulProperties;
    @Autowired
    private AuthenticationSuccessHandler appAuthenticationSuccessHandler;
    @Autowired
    private AuthenticationFailureHandler appAuthenticationFailureHandler;
    @Autowired
    private AccessDeniedHandler appAccessDeniedHandler;

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



    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
            .antMatchers(HttpMethod.GET,zuulProperties.getAuth().toGetAdapter())
            .permitAll()
            .and()
                .authorizeRequests()
                .antMatchers(HttpMethod.POST,zuulProperties.getAuth().toPostAdapter())
                .permitAll()
            .and()
                .authorizeRequests()
                .anyRequest()
                .authenticated()
            .and()
                .exceptionHandling().accessDeniedHandler(appAccessDeniedHandler)
            .and()
                .csrf().disable();
    }

spring oauth相关

 @Configuration
@EnableAuthorizationServer
public class AppAuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private final AuthenticationManager authenticationManager;
    @Autowired
    private UserDetailsService userDetailsService;
    @Autowired
    private ZuulProperties zuulProperties;
    @Autowired
    private TokenStore tokenStore ;
    @Autowired(required = false)
    private JwtAccessTokenConverter jwtAccessTokenConverter;
    @Autowired(required = false)
    private TokenEnhancer jwtTokenEnhancer;
    @Autowired
    private PasswordEncoder passwordEncoder;

    public AppAuthorizationServerConfig(AuthenticationManager authenticationManager) {
        this.authenticationManager = authenticationManager;
    }


    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        InMemoryClientDetailsServiceBuilder builder = clients.inMemory();
        OAuth2ClientProperties[] clientProperties = zuulProperties.getOauth().getClients();
        if(ArrayUtils.isNotEmpty(zuulProperties.getOauth().getClients())) {
            for (OAuth2ClientProperties oAuth2ClientProperties : clientProperties) {
                 builder.withClient(oAuth2ClientProperties.getClientId())
                   .secret(oAuth2ClientProperties.getClientSecret())
                   //token有效时间
                   .accessTokenValiditySeconds(oAuth2ClientProperties.getAccessTokenValiditySeconds())
                   //验证模式
                   .authorizedGrantTypes("password","authorization_code","client_credentials","implicit","refresh_token")
                   //刷新时间
                   .refreshTokenValiditySeconds(3600*24*100)
                    //跳转地址
                   .redirectUris("ws.28ph.cn")
                   //权限
                   .scopes("all");
            }
        } 
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
        endpoints.authenticationManager(authenticationManager)
                .tokenStore(tokenStore)
                .userDetailsService(userDetailsService)
                .reuseRefreshTokens(true);
        if(jwtAccessTokenConverter != null && jwtTokenEnhancer!=null) {
            TokenEnhancerChain enhancerChain = new TokenEnhancerChain();
            List<TokenEnhancer> tokenEnhancers = new ArrayList<>();
            tokenEnhancers.add(jwtTokenEnhancer);
            tokenEnhancers.add(jwtAccessTokenConverter);
            enhancerChain.setTokenEnhancers(tokenEnhancers);
            endpoints
                    .tokenEnhancer(enhancerChain)
                    .accessTokenConverter(jwtAccessTokenConverter);
        }
    }


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

}

问题的产生:我在spring security配置了几个不需要拦截的uri,但是加入spring oauth 全部被拦截了,如果删掉@EnableResourceServer会出现 发出来的token 认证不了url的问题。。

然后我想大不了不用spring oauth 自带的发token方式。然后我在spring security的成功handler上下发token ,以下是代码

/**
 * 认证成功跳转
 * @author w4837
 *
 */
@Component(value = "AppAuthenticationSuccessHandler")
@Slf4j
public class AppAuthenticationSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {

    @Autowired
    private ObjectMapper objectMapper;
    @Autowired
    private ClientDetailsService clientDetailsService;
    @Autowired
    private AuthorizationServerTokenServices authorizationServerTokenServices;



    @Override
    public void onAuthenticationSuccess(HttpServletRequest httpServletRequest,
                    HttpServletResponse httpServletResponse, Authentication authentication)
            throws IOException, ServletException {
        log.info("登陆成功");
        String header = httpServletRequest.getHeader("Authorization");
        //请求头包含Authorization 并且以"Basic "开始
        if (header == null || !header.startsWith("Basic ")) {
            throw new UnapprovedClientAuthenticationException("请求头中无Authorization信息");
        }

        try {
            String[] tokens = extractAndDecodeHeader(header, httpServletRequest);
            assert tokens.length == 2;

            String clientId = tokens[0];
            String clientSecret = tokens[1];
            ClientDetails clientDetails = clientDetailsService.loadClientByClientId(clientId);
            if(clientDetails == null) {
                throw new UnapprovedClientAuthenticationException("clientId:"+clientId+"对应的信息不存在。");
            }else if(!StringUtils.equals(clientSecret, clientDetails.getClientSecret())) {
                throw new UnapprovedClientAuthenticationException("clientId:"+clientId+"对应的信息不匹配。");
            }
            @SuppressWarnings("unchecked")
            TokenRequest tokenRequest = new TokenRequest(MapUtils.EMPTY_MAP, clientId, clientDetails.getScope(),"custom");

            OAuth2Request auth2Request = tokenRequest.createOAuth2Request(clientDetails);

            OAuth2Authentication auth2Authentication = new OAuth2Authentication(auth2Request, authentication);

            OAuth2AccessToken createAccessToken = authorizationServerTokenServices.createAccessToken(auth2Authentication);
            // 判断需要的返回类型
            httpServletResponse.setContentType(ZuulAppConstant.CONTENT_TYPE_JSON);
            httpServletResponse.getWriter().write(objectMapper.writeValueAsString(createAccessToken));
        } catch (Exception e) {
            log.error(e.getMessage());
        }
    }

    /**
     * 解析header中编码后的数据
     *
     * @param header
     * @param request
     * @return
     * @throws IOException
     */
    private String[] extractAndDecodeHeader(String header, HttpServletRequest request) throws IOException {

        byte[] base64Token = header.substring(6).getBytes("UTF-8");
        byte[] decoded;
        try {
            decoded = Base64.decode(base64Token);
        } catch (IllegalArgumentException e) {
            throw new BadCredentialsException("Failed to decode basic authentication token");
        }

        String token = new String(decoded, "UTF-8");

        int delim = token.indexOf(":");

        if (delim == -1) {
            throw new BadCredentialsException("Invalid basic authentication token");
        }
        return new String[] { token.substring(0, delim), token.substring(delim + 1) };
    }

结果 启动还是报循环依赖的错误


***************************
APPLICATION FAILED TO START
***************************

Description:

The dependencies of some of the beans in the application context form a cycle:

┌─────┐
|  appAuthorizationServerConfig defined in file [F:\yulece_aike_ideaword\app-management\app-management-zuul\target\classes\com\yulece\app\management\zuul\authorization\AppAuthorizationServerConfig.class]
↑     ↓
|  appWebSecurityConfigurerAdapter (field private org.springframework.security.web.authentication.AuthenticationSuccessHandler com.yulece.app.management.zuul.authorization.AppWebSecurityConfigurerAdapter.appAuthenticationSuccessHandler)
↑     ↓
|  AppAuthenticationSuccessHandler (field private org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices com.yulece.app.management.zuul.authorization.handler.AppAuthenticationSuccessHandler.defaultAuthorizationServerTokenServices)
↑     ↓
|  org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerEndpointsConfiguration (field private java.util.List org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerEndpointsConfiguration.configurers)
└─────┘

0
Csdn user default icon
上传中...
上传图片
插入图片
抄袭、复制答案,以达到刷声望分或其他目的的行为,在CSDN问答是严格禁止的,一经发现立刻封号。是时候展现真正的技术了!
其他相关推荐
在web项目中spring security与cas 结合使用的意义
在进行spring security的描述之前,我想描述一下认证与授权两个概念。 针对于企业级web应用中一个登陆过程,需要完成什么功能。其实就是完成认证与授权。在基于form表单的登陆形式,用户进入系统,在这个过程需要完成认证与授权。 所谓认证,就是当用户试图进入系统,而系统发现用户没有登陆,就调转到登陆页面,然后用户输入用户名,密码,点击登陆按钮,系统进行用户名,密码的 校验过程,称之为...
自实现oauth2验证与spring-security的结合
自实现oauth2验证与spring-security的结合 前言 前面写了一篇关于spring-security-oauth2适配的文章,但是种种原因,项目中正在使用的spring-security版本暂时不能更换,没法直接使用spring-security-oauth2,无奈只能自己实现验证过程。 本文主要总结一下自实现oauth2验证遇到的一些问题及解决办法。 一、自实现...
spring security和oauth2整合开发资料汇总
spring security和oauth2整合开发资料汇总,自己总结收集的。
spring Security4 和 oauth2整合 注解+xml混合使用(替换用户名密码认证)
spring Security4 和 oauth2整合 (替换用户名密码认证)之前已经写了注解和xml配合搭建基本认证、页面认证、授权码认证、替换6位授权码方法等,这次在前面的基础上介绍如何替换用户名密码认证,下一篇介绍如何增加验证码等额外参数验证方法。 代码比较多,这次只贴出来部分,完整代码在 https://gitee.com/xiaoyaofeiyang/OauthUmp
spring Security4 和 oauth2整合 注解+xml混合使用(注意事项篇)
Spring Security4 和 oauth2整合注意事项注意事项
Spring security oauth2最简单入门环境搭建--二、干货 博客分类: OAuth2 spring security oauth入门配置oauth2教程 关于OAuth2的一些简介
Spring security oauth2最简单入门环境搭建--二、干货 博客分类:  OAuth2 spring security oauth入门配置oauth2教程  关于OAuth2的一些简介,见我的上篇blog:http://wwwcomy.iteye.com/blog/2229889 PS:貌似内容太水直接被鹳狸猿干沉。。  友情提示 学习曲线:spr
spring Security4 和 oauth2整合 注解+xml混合使用(验证码等额外数据验证)
spring Security4 和 oauth2整合(验证码等额外数据验证)上一篇写的自定义用户名密码验证,这里写验证码的验证,或者其他信息的验证。验证码等额外数据获取逻辑新增OauthAddUserDetails继承WebAuthenticationDetails,这里从request里拿到额外的参数。对比验证码,我们需要有一个接口去更新验证码,更新完放到session里
Spring Security 与 OAuth2(授权服务器)
authrization-server(授权服务器) 授权服务配置 配置一个授权服务,需要考虑 授权类型(GrantType)、不同授权类型为客户端(Client)提供了不同的获取令牌(Token)方式,每一个客户端(Client)都能够通过明确的配置以及权限来实现不同的授权访问机制,也就是说如果你提供了一个 “client_credentials” 授权方式,并不意味着其它客户端就要采用...
springCloud+security+oauth+zuul
springCloud+security+oauth+zuul整合demo。《springBoot实战》 汪云飞demo整合。
【Spring Security OAuth2笔记系列】- Security控制授权- 权限表达式
权限表达式 看源码得知,最后都会转成一个表达式,然后进行投票评估; 那么有哪些表达式呢? 这些表达式的由来,由代码中的配置而来。 .antMatchers().xxx 每个函数都包装了一个表达式生成。 跟着源码得到 返回的是一个 ExpressionUrlAuthorizationConfigurer.AuthorizedUrl 对象 联合使用是通过access方法,自己写...
spring Security4 和 oauth2整合 注解+xml混合使用(授权码篇)
Spring Security4 和 oauth2整合授权码模式上两篇介绍了环境配置和用户密码模式,下面介绍授权码模式。
springboot与security oauth2整合例子
springboot与security oauth2+jwt控制安全整合例子。。
Spring security 集成ldap服务,实现统一验证
先说一下Spring security 是基于spring的一个强大的安全验证模块,它提供了一组可以在Spring应用上下文中配置的Bean,充分利用了Spring IoC,DI(控制反转Inversion of Control ,DI:Dependency Injection 依赖注入)和AOP(面向切面编程)功能,为应用系统提供声明式的安全访问控制功能。 LDAP是轻量目录访问协议,基于tc
spring security oauth2 令牌生成源码解析
spring security oauth2 令牌生成流程为(蓝色字体为类,红色字体为接口):/oauth/token/请求  --&amp;gt; TokenEndpoint --&amp;gt;  ClientDetailsService(InMemoryClientDetailsService) --&amp;gt; ClientDetails --&amp;gt; TokenRequest --&amp;gt; TokenGra...
基于 Spring Boot 的 SSM 环境整合十六:整合 spring security 三(自定义登录结果)
这一篇主要研究登录成功、登录失败时如何返回结果。 1、方法一:在 WebSecurityConfigurerAdapter 实现类中定义 首先回顾下前文中SecurityConfig类的部分配置: @Override protected void configure(HttpSecurity http) throws Exception { System.out.println(&quot;...
spring Security4 和 oauth2整合 注解+xml混合使用(进阶篇)
Spring Security4 和 oauth2整合用户密码授权模式上篇已经可以正常运行了,不过拿来测试还不够,下面介绍如何测试oauth2的用户密码模式,授权码模式下一篇说。
Re:从零开始的Spring Security Oauth2(三)
上一篇文章中我们介绍了获取token的流程,这一篇重点分析一下,携带token访问受限资源时,内部的工作流程。@EnableResourceServer与@EnableAuthorizationServer还记得我们在第一节中就介绍过了OAuth2的两个核心概念,资源服务器与身份认证服务器。我们对两个注解进行配置的同时,到底触发了内部的什么相关配置呢?上一篇文章重点介绍的其实是与身份认证相关的流程,
【Spring Security OAuth2笔记系列】- Spring Social第三方登录 - 退出登录
退出登录 如何退出登录 Spring security 默认的退出处理逻辑 与退出登录相关的配置 默认退出处理逻辑 使当前session失效 清除与当前用户相关的remember-me记录 清空当前的SecurityContext 重定向到登录页 还记得以前登录的时候有一个默认的登录地址:/login,同样默认了一个退出/logout; 直接访问该地址:如果看到下面的报错,请...
用 spring security oauth2进行用户认证,如何更新Redis中的用户信息
用 spring security oauth2进行用户认证,首次登录后得到access_token和refresh_token,然后修改SecurityContextHolder.getContext().getAuthentication()中的Principal信息,然后_再次用access_token 从SecurityContextHolder.getContext().getAuthe...
Springboot Security Oauth2 第一篇:构建一个简单例子
本文主要讲解了Springboot集成Security Oauth2的一个简单例子,通过该例子进而学习该方面的知识。 内容简介:使用password密码授权方式,让用户通过账号密码去服务器获取token,并通过token来访问接口。 技术:springboot2.x ,springsecurity5.x
关于 Nginx 反向代理导致 Spring Boot OAuth2 认证失败的问题
最近在给 Docs4dev 添加用户评论功能时,使用了 Github 提供的 OAuth2 认证来进行用户身份认证,在本地开发环境中一切正常,但是一放到服务器就会认证失败,查看日志后发现 OAuth2 的 redirectUri 参数不匹配,在添加了相关日志后,发现 Spring Boot 是通过 UrlUtils.buildFullRequestUrl(request) 从 HttpServle...
Spring boot 入门教程-在Spring Security+Oauth2.0(密码模式、客户端模式)
OAuth是一个关于授权(authorization)的开放网络标准,在全世界得到广泛应用,目前的版本是2.0版。 实现思路:OAuth在&quot;客户端&quot;与&quot;服务提供商&quot;之间,设置了一个授权层(authorization layer)。&quot;客户端&quot;不能直接登录&quot;服务提供商&quot;,只能登录授权层,以此将用户与客户端区分开来。&quot;客户端&quot;登录授权层所用的令牌(token),与用户的密码不同。用户可以在登录的时候,...
关于异常处理以及针对spring security oauth 2的401错误进行页面跳转
由于最近遇到了新问题,还折磨了我两天,所以这里就简单的记录一下⑧ 错误集中处理 由于系统需要,所以为项目添加了一个错误集中处理配置 前情提要 spring boot : 2.0.3.RELEASE 参考文章 首先根据spring boot版本的不同,以1.4.0为界是有不同的配置方式的,这里由于用的是2.0.3.RELEASE,所以配置如下。 package com.yubotao.spring...
Springboot通过cors解决跨域问题(解决spring security oath2的/oauth/token跨域问题)
在工程里添加两个类: CorsConfig.java: 实现全局过滤器,设置CORS,注意一定要是全局。网上说多加一个注解(Spring官网)或者加Cors Mapper只能解决自定义接口的跨域,对于spring security oath2的默认接口,例如 /oauth/token跨域问题,是无法解决的,必须通过本文的全局CORS Filter解决。package com.qiaoya.inte
第九章 SpringCloud Oauth2认证中心-Zuul网关上添加认证
本章完整源码地址:https://github.com/kwang2003/springcloud-study-ch09.git 1.项目概要 这一章节的内容以第七章的代码为基础改造而成https://github.com/kwang2003/springcloud-study-ch08.git。 通过第八章的学习,我们已经已经基于JWT升级了OAuth2认证服务器,在这个章节中
零碎记录- spring security oauth2 资源服务器中设置放行路径
在资源服务器配置类中重写configure方法,添加放行信息 使用了@EnableResouceServer,且继承了ResourceServerConfigurerAdapter的类作为资源服务器配置信息类 @Configuration @EnableResourceServer @EnableGlobalMethodSecurity(prePostEnabled = true) clas...
Spring boot+Security OAuth2 爬坑日记(3)自定义登录和授权页面
1. 依赖 &amp;amp;amp;amp;lt;!---------thymeleaf 模板引擎--------&amp;amp;amp;amp;gt; &amp;amp;amp;amp;lt;dependency&amp;amp;amp;amp;gt; &amp;amp;amp;amp;lt;groupId&amp;amp;amp;amp;gt;org.springframework.boot&amp;amp;amp;amp;lt;/groupId&amp;amp;amp;amp;gt;
Re:从零开始的Spring Security Oauth2(一)
前言今天来聊聊一个接口对接的场景,A厂家有一套HTTP接口需要提供给B厂家使用,由于是外网环境,所以需要有一套安全机制保障,这个时候oauth2就可以作为一个方案。关于oauth2,其实是一个规范,本文重点讲解spring对他进行的实现,如果你还不清楚授权服务器,资源服务器,认证授权等基础概念,可以移步理解OAuth 2.0 - 阮一峰,这是一篇对于oauth2很好的科普文章。 需要对spring
springboot学习笔记(十二) SpringBoot项目中使用SpringSecurity整合OAuth2设计项目API安全接口服务
OAuth是一个关于授权的开放网络标准,在全世界得到的广泛的应用,目前是2.0的版本。OAuth2在“客户端”与“服务提供商”之间,设置了一个授权层(authorization layer)。“客户端”不能直接登录“服务提供商”,只能登录授权层,以此将用户与客户端分离。“客户端”登录需要OAuth提供的令牌,否则将提示认证失败而导致客户端无法访问服务。下面我们就来讲解下SpringBoot项目中是...
Spring Security Oauth2 认证(获取token/刷新token)流程(password模式)
1.本文介绍的认证流程范围 本文主要对从用户发起获取token的请求(/oauth/token),到请求结束返回token中间经过的几个关键点进行说明。 2.认证会用到的相关请求 注:所有请求均为post请求。 获取access_token请求(/oauth/token) 请求所需参数:client_id、client_secret、grant_type、username、passwo...
使用Spring Security和OAuth2实现RESTful服务安全认证
使用Spring Security和OAuth2实现RESTful服务安全认证    这篇教程是展示如何设置一个OAuth2服务来保护REST资源. 源代码下载github. 你能下载这个源码就开始编写一个被OAuth方法保护的服务。该源码包含功能: * 用户注册和登录 * Email验证 * Password 丢失 采取的技术有以下: * OAuth2 Protoc
Security-OAuth2 密码模式之服务器实现(工具IDEA+Maven+springboot)
OAuth2 密码模式之服务器实现第一步:配置数据库 ,固定创建三张表 ,OAuth2 框架需要默认使用这三张表 我使用的时Mysql,工具为navcatCREATE TABLE `oauth_access_token` ( `token_id` varchar(256) DEFAULT NULL, `token` blob, `authentication_id` varchar
【Spring Security OAuth2笔记系列】- spring security -短信登录配置及重构
短信登录配置及重构 重构思路: 重构不是更改已有的功能 重构是不影响已有功能的情况下,对已有代码进行抽象封装 多处使用相同代码的地方,需要抽出来 比如上章节的很多代码, 如:图形验证码过滤器和短信验证码过滤器重复代码太多 服务接口的url地址和过滤器中的过滤器地址重复 等.. 系统配置相关的代码结构 core项目中的重构如下: 密码登录的配置代码 短信登录的配置代码 验证...
基于Spring Security的Oauth2授权实现
前言 经过一段时间的学习Oauth2,在网上也借鉴学习了一些大牛的经验,推荐在学习的过程中多看几遍阮一峰的《理解OAuth 2.0》,经过对Oauth2的多种方式的实现,个人推荐Spring Security和Oauth2的实现是相对优雅的,理由如下: 1、相对于直接实现Oauth2,减少了很多代码量,也就减少的查找问题的成本。 2、通过调整配置文件,灵活配置Oauth相关配置。 3、通过...
spring-security-oauth2 中优雅的扩展自定义(短信验证码)登录方式【Part-End】
上一篇 中我们讨论了如何实现的思路,本篇中我们把它实现 发送验证码的Controller 首先我们需要创建一个发送验证码的 Controller, 至于如何实现,这里就不多说了,大家都会的,本篇重点说明验证部分. 【注意】在认证服务器上增加自己的 Controller, 默认情况下访问是返回403,有两种办法解决: 把认证服务器也配制为资源服务器,既: 它既是认证服务器,也是资源服务器.并配制新...
如何使用Google作为认证方配置Spring Boot 2 Security5集成的OAuth2登录我们自己的工程项目------范例1
Google客户端授权生成client-id和client-secret我们需要登录以下地址 https://console.developers.google.com 第1部分: 范例工程项目结构如下: Enabling OAuth 2 login Suppose that you want to enable users of your application to be ab...
spring Security4 和 oauth2整合 注解+xml混合使用(基础运行篇)
Spring Security4 和 oauth2整合最近项目中需要用到oauth2,到网上找了好多资料,全是乱七八糟的,东拼西凑,终于跑出来了一版,xml的方式太乱了,跑不了,还是用注解方式,并把一些关键配置提到xml中。git地址:https://gitee.com/xiaoyaofeiyang/OauthUmpspring Security4 和 oauth2整合 注解+xml混合使用
Spring boot+Security OAuth2 爬坑日记(5)自定义异常处理 下
在上篇定义了两个问题 记录问题: filter 如何实现 在哪里注入我们自己定义的filter 家下来就是去实现我们自己的filter
简单说说Spring Security 使用(附加验证码登录,自定义认证)
先看官方文档:http://docs.spring.io/spring-security/site/docs/4.0.x/reference/htmlsingle/ spring security4已经加入了注解的方式,但是为了比较清晰了解,还是使用了配置的方式。 第一步:web.xml 加入拦截、 &lt;!-- 配置springSecurityFilter --&...
SpringSecurity Oauth 进行登录时跳过授权
登录后就不需要跳转到验证页进行授权了.autoApprove(true);                  //登录后绕过批准询问(/oauth/confirm_access)/** * 配置客户端详情 * @param clients * @throws Exception */ @Override public void configu...
文章热词 机器学习教程 Objective-C培训 交互设计视频教程 颜色模型 设计制作学习
相关热词 mysql关联查询两次本表 native底部 react extjs glyph 图标 深度学习结合大数据 数据库课程结合