No4.搭建基本的授权码模式请求token(一):实现授权服务端的授权码模式操作

代码地址与接口看总目录:【学习笔记】记录冷冷-pig项目的学习过程,大概包括Authorization Server、springcloud、Mybatis Plus~~~_清晨敲代码的博客-CSDN博客

目录

授权服务端授权码模式动图操作

前言

A1.分析开放平台和第三方应用之间的逻辑流程

B1.逻辑流程图

B2.逻辑流程文字说明

B3.最终需要实现的内容

A2.框架搭建

B1.授权服务端类

A3.授权服务端代码实现

B1.配置类

C1.AuthorizationServerConfiguration

C2.RegisteredClientConfiguration

C3.WebSecurityConfiguration

 C4.FormIdentityLoginConfigurer

C6.application.yml

B2.认证授权核心类

C1.业务类

D1.OAuth2AuthorizationEndpointFilter

D2.OAuth2AuthorizationCodeRequestAuthenticationConverter

D3.OAuth2AuthorizationCodeRequestAuthenticationProvider

D4.OAuth2ClientAuthenticationFilter

D5.ClientSecretAuthenticationProvider

D6.OAuth2TokenEndpointFilter

D7.OAuth2AuthorizationCodeAuthenticationConverter

D8.OAuth2AuthorizationCodeAuthenticationProvider

D9.CustomeOAuth2AccessTokenGenerator

C2.处理类

D1.FormAuthenticationFailureHandler

D2.SsoLogoutSuccessHandler

B3.端点类

 D1.PigTokenEndpoint

B5.页面

A5.客户端后端代码实现(待实现)


授权服务端授权码模式动图操作

8ee4ac9f68ed478ea02f8294194e9473.gif

前言

我们要做的是:一个第三方登录端,一个前后端分离的客户端;第三方登录端就相当于提供授权的开放平台,客户端就相当于使用开放平台的第三方应用;

(只做前后端分离的后端部分)

前几篇文章我们只是使用OAuth2的密码模式,实现了一个微服务环境(还不算真正的微服务,就是实现不同服务之间的调用而已)下的sso单点登录,现在是要将这个sso单点登录系统作为一个开放平台,去开放授权码认证模式,让其他第三方应用可以通过它授权使用。

A1.分析开放平台和第三方应用之间的逻辑流程

B1.逻辑流程图

在开始之前,先分析一下开放平台和第三方应用之间的逻辑流程:

70e9ef1e1ab448e0a398558e6a64bae5.png

e75078016e1b48268e5fa6e98b5c5da7.png

B2.逻辑流程文字说明

1.用户向客户端前端发起使用授权服务端登录授权请求;

2.客户端前端收到使用授权服务端登录授权请求后,返回一个向授权服务端发起登录授权的请求链接(该链接会携带客户端id、登录授权类型、登录授权范围、登录授权回调地址等)【此处可以直接在前端保存链接的参数但是不安全,也可以调用客户端后端拿到参数】;

3.用户拿着上面的链接向授权服务端发起登录授权请求;

4.授权服务端校验到该用户没有登陆,就返回登录页面;

5.用户填写登录信息向授权服务端发起登录;

6.授权服务端校验登录信息成功后,会再次返回给用户登录授权的请求链接,让用户再次重定向;

7.用户再次拿着上面的链接向授权服务端发起登录授权请求;

8.授权服务端根据请求,若判断用户已授权的权限<客户端持有的权限(这一块逻辑可自定义),就返回授权确认页面;

9.用户进行授权确认后,将授权的信息返回给授权服务端;

10.授权服务端校验无误后,会将code拼接到登录授权回调地址,然后返回给用户;

11.用户拿着上面的链接会向客户端前端发起请求;

12.客户端前端拿着code调用客户端后端;

13.客户端后端拿到code,向授权服务端获取token;

14.授权服务端校验code成功后向客户端后端返回token和用户信息;

15.客户端后端获取到token,去数据库中获取绑定账号信息;

16.数据库将绑定信息返回给客户端后端,客户端后端发现该授权服务端的用户没有绑定到自己库中的账号;

17.客户端后端返回客户端前端一个信号;

18.客户端前端返回给用户一个绑定账号页面;

19.用户输入绑定账号需要的信息,发给客户端前端;

20.客户端前端将信息发给客户端后端;

21.客户端后端将账号信息与授权服务端的用户信息绑定,并保存到库中;

22.数据库返回执行语句的返回信息给客户端后端;

23.客户端后端将用户信息进行一系列登录认证保存后,返回给客户端前端token等信息;

24.客户端前端拿到token进行一系列保存后,返回给用户一个默认页面(或用户一开始请求的页面)

25.用户携带着客户端token向客户端前端发起业务功能请求;

26.客户端前端携带着token向客户端后端发起业务功能请求;

27.客户端后端会进行校验客户端token,如果是自己的业务功能,则直接返回给客户端前端;

28.客户端前端根据返回的信息,打开指定页面;

29.如果是授权服务端的公开接口则客户端后端会从客户端token中拿到授权服务端的token,然后端携带着token向授权服务端发起业务功能请求;

30.授权服务端返回给客户端后端确认token有效后返回信息以及用户信息;

31.客户端后端保存用户信息,执行业务功能,并返回给客户端前端;

32.客户端前端根据返回的信息,打开指定页面;

B3.最终需要实现的内容

所以,我们整理之后发现流程会有:

授权服务端需要有:

1.登录的请求页面;

2.登录请求;

3.授权确认页面;

4.确认授权请求;

5.获取token的接口;

6.校验token的接口;

客户端前端需要有:

1.向授权服务器发起授权请求的页面操作;

2.接收code的回调地址/页面;

3.携带code向客户端后端发起认证的请求,拿到返回值token后记得保存;

4.绑定用户页面(当客户端后端拿到token和用户信息时发现未绑定到账号时返回的页面);

客户端后端需要有:

1.接收客户端前端code并携带code向授权服务端获取token,然后处理用户认证登录并返回token的接口(大多用拦截器拦截处理这个操作);

2.根据access_token和用户信息进行用户登录绑定(就不做注册绑定了);

3.发起校验token的操作(需要执行授权服务端业务接口时执行的操作);

注意:springsecurity authorization 授权服务端的登录认证过滤链(还有一个是认证授权过滤链)是使用的默认的session机制,即通过SessionManagementConfigurer创建一个HttpSessionSecurityContextRepository来持久化session信息;

如果想不使用session,可以自己实现一个过滤器处理用户登录

(例如使用JWT,还需要

1.后续请求中需要携带jwt_token,

2.自定义一个拦截器拦截jwt_token并获取用户信息到SecurityContextHolder中,

3.给httpsecurity设置http.sessionManagement()

.sessionCreationPolicy(SessionCreationPolicy.STATELESS);,这样就不会持久化session信息了,

4.没有登录用户持久化就得自己写一个,可以使用redis等进行存储)

现在直接使用默认的session机制。

A2.框架搭建

B1.授权服务端类

79d4da6a1ea14c50bd33f58dcd080cc6.png

A3.授权服务端代码实现

B1.配置类

C1.AuthorizationServerConfiguration

1.添加授权码模式的授权端点配置,和添加统一认证的配置。

授权码模式的授权端点配置是为了客户端访问时能拿到code值,然后携带code值获取accesstoken,获取accesstoken还是用原来的认证授权端点就可以,会直接使用我们自定义的token生成器。

统一认证的授权端点是为了访问这个端点时,如果未登录能直接跳转到指定登录页面;

2.添加授权码模式涉及到的Authentication转换器,都使用authorization-server自带的,一个是处理获取code的,一个是获取token的。

@Bean@Order(Ordered.HIGHEST_PRECEDENCE)public SecurityFilterChain authenticationServerSecurityFilterChain(HttpSecurity http) throws Exception {。。。//配置处理授权码模式认证过程中获取 code 的请求http.apply(authorizationServerConfigurer.authorizationEndpoint(authorizationEndpoint -> {// 配置授权码端点个性化confirm页面authorizationEndpoint.consentPage(SecurityConstants.CUSTOM_CONSENT_PAGE_URI);// 注入个性化的授权认证ConverterauthorizationEndpoint.authorizationRequestConverter(accessTokenRequestConverter());}));//设置为统一的自定义表单登录配置http.apply(new FormIdentityLoginConfigurer());。。。return securityFilterChain;}public AuthenticationConverter accessTokenRequestConverter(){//new一个token转换器委托器,其中包含自定义密码模式认证转换器和刷新令牌认证转换器return new DelegatingAuthenticationConverter(Arrays.asList(。。。// 访问令牌请求用于OAuth 2.0授权码授权   ——授权码模式获取tokennew OAuth2AuthorizationCodeAuthenticationConverter(),//  授权请求(或同意)用于OAuth 2.0授权代码授权   ——授权码模式获取codenew OAuth2AuthorizationCodeRequestAuthenticationConverter()));}

C2.RegisteredClientConfiguration

 重新创建一个客户端对象,重点是设置授权方法为授权码模式。然后并添加到客户端持久层里面。

    /*** @Description: 注册一个客户端应用** @return the registered client repository*/@Beanpublic RegisteredClientRepository registeredClientRepository() {//new一个客户端管理业务,这里用的基于内存持久的,一定要注入一个客户端信息,否则无法使用//每次启动都会初始化的;生产的话,只初始化RegisteredClientRepository,不初始化里面的客户端信息InMemoryRegisteredClientRepository inMemoryRegisteredClientRepository = new InMemoryRegisteredClientRepository(this.createRegisteredClient_umps("8081"),this.createRegisteredClient_client("8082"));return inMemoryRegisteredClientRepository;}/*** @Description: 创建客户端信息* @param id* @return*/private RegisteredClient createRegisteredClient_client(final String id) {return RegisteredClient.withId(UUID.randomUUID().toString())
//               客户端ID和密码.clientId("client")
//               此处为了避免频繁启动重复写入仓库.id(id).clientSecret(PasswordEncoderFactories.createDelegatingPasswordEncoder().encode("client"))
//                名称 可不定义.clientName("client")
//                授权方法.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
//                授权类型.authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN).authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
//                回调地址名单,不在此列将被拒绝 而且只能使用IP或者域名  不能使用 localhost.redirectUri("http://127.0.0.1:8082/login/oauth2/code/qingchen-client").redirectUri("https://pig4cloud.com")
//                其它Scope.scope("client")
//                JWT的配置项 包括TTL  是否复用refreshToken等等.tokenSettings(TokenSettings.builder().accessTokenFormat(OAuth2TokenFormat.REFERENCE).build())
//                配置客户端相关的配置项,包括验证密钥或者 是否需要授权页面.clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build()).build();}

C3.WebSecurityConfiguration

由于授权码模式,是需要用户在开放平台进行登录,然后授权跳转到客户端应用。

所以把授权服务端默认的表单登陆认证模式也改成我们统一的认证模式,并添加userservice,否则会登陆不上去~

由于自定义登录页面和授权页面,涉及到访问css等静态资源样式,所以就直接将静态资源设置为permit,并且不需要认证等操作。


@RequiredArgsConstructor
@Configuration(proxyBeanMethods = false)
public class WebSecurityConfiguration {private final UserDetailsService userDetailsService;/*** @Description: spring security 默认的安全策略* @param http* @Return: org.springframework.security.web.SecurityFilterChain*/@BeanSecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {//根据业务开放自定义的部分端点//其余端点都需要登录//配置自定义用户认证提供方http.authorizeRequests(authorizeRequests -> authorizeRequests.antMatchers("/token/*").permitAll() // 开放自定义的部分端点.anyRequest().authenticated()); // 其余都需要登录//设置为统一的自定义表单登录配置http.apply(new FormIdentityLoginConfigurer());// 处理 UsernamePasswordAuthenticationTokenPigDaoAuthenticationProvider pigDaoAuthenticationProvider = new PigDaoAuthenticationProvider();pigDaoAuthenticationProvider.setUserDetailsService(userDetailsService);http.authenticationProvider(pigDaoAuthenticationProvider);return http.build();}/*** 暴露静态资源** https://github.com/spring-projects/spring-security/issues/10938* @param http* @return* @throws Exception*/@Bean@Order(0)SecurityFilterChain resources(HttpSecurity http) throws Exception {http.requestMatchers((matchers) -> matchers.antMatchers("/actuator/**", "/css/**", "/error")).authorizeHttpRequests((authorize) -> authorize.anyRequest().permitAll()).requestCache().disable().securityContext().disable().sessionManagement().disable();return http.build();}
}

 C4.FormIdentityLoginConfigurer

由于前两个配置(认证授权过滤链和默认过滤链)都会涉及到认证登录,所以我们就进行一下统一,把这一块抽象出来,就可以多处复用了。

这一块儿主要的逻辑是:

1.配置表单认证模式,并且自定义访问登录接口、提交登录接口、设置登录失败处理器;这里的访问登录接口是自定义的端点,并且返回的是一个页面。

2.配置登出,设置登出成功处理器,删除cookies和session

3.设置 csrf 为无效(否则会出现跨域问题,如何解决跨域问题,后面涉及到了再具体分析)

public class FormIdentityLoginConfigurer extends AbstractHttpConfigurer<FormIdentityLoginConfigurer, HttpSecurity> {@Overridepublic void init(HttpSecurity http) throws Exception {http.formLogin(httpSecurityFormLoginConfigurer -> {httpSecurityFormLoginConfigurer.loginPage("/token/login");// 统一认证的登录页面 urlhttpSecurityFormLoginConfigurer.loginProcessingUrl("/token/form");//统一登录授权actionhttpSecurityFormLoginConfigurer.failureHandler(new FormAuthenticationFailureHandler());});http.logout(httpSecurityLogoutConfigurer -> {// SSO登出成功处理httpSecurityLogoutConfigurer.logoutSuccessHandler(new SsoLogoutSuccessHandler()).deleteCookies("JSESSIONID");//一个显式添加CookieClearingLogoutHandler的快捷方式。httpSecurityLogoutConfigurer.invalidateHttpSession(true);});http.csrf().disable();}
}

C6.application.yml

配置文件,主要配置Freemarker相关的

# freemark配置
spring:freemarker:allow-request-override: falseallow-session-override: falsecache: truecharset: UTF-8check-template-location: truecontent-type: text/htmlenabled: trueexpose-request-attributes: falseexpose-session-attributes: falseexpose-spring-macro-helpers: trueprefer-file-system-access: truesuffix: .ftltemplate-loader-path: classpath:/templates/

B2.认证授权核心类

C1.业务类

业务类没有需要我们增加的,一部分用authorization-server自带的,一部分用NO1中密码模式添加的。所以到授权码模式这里,我们就没有要新增的了。

但是为了以防我忘掉,还是过一下都需要哪些类吧~

1.authorization-server自带的

D1.OAuth2AuthorizationEndpointFilter

获取授权码模式的code的过滤器,默认拦截的是"/oauth2/authorize"。拦截之后进行操作,只有路径匹配不上和路径匹配上但是授权认证不通过才会进入下一个过滤器,其余情况直接调用处理器。

授权认证通过后会有两种情况,一种是1.当前请求需要确认授权;2.当前请求已确认授权。

1.就是现在认证成功啦,但是需要用户进行授权,就会返回一个确认授权页面;(一般GET请求会走这个)

2.当前的请求本身就是确认授权请求的操作,那么就处理并返回code;(一般POST请求会走这个)

至于如何确认走那个情况,是由D3.确定的。

D2.OAuth2AuthorizationCodeRequestAuthenticationConverter

在D1.中被拦截到调用的,会通过这个类将request信息转换成想要的OAuth2AuthorizationCodeRequestAuthenticationToken,此时是未认证的哦~(仅仅是将信息转化,并没有进行认证授权)

此处我们用的是authorization-server提供的,那么就需要按照这个类的要求来设置接口属性和入参,当然也可以自定义,不过就需要修改对应的filter了

D3.OAuth2AuthorizationCodeRequestAuthenticationProvider

也是在D1.中被拦截到调用的,会对D2.提供的AuthenticationToken进行认证。

这里有两个情况:1.当前请求需要确认授权;2.当前请求确认授权。是否确认是由D2.converter确定的!

在设置为需要确认授权(是在创建客户端对象的时通过.clientSettings()设置的)的情况下:

当通过GET来访问"/oauth2/authorize"时,会设置为consent = false,会走1.当前请求需要确认授权,会创建一个已认证的AuthenticationToken,同时设置consentRequired = true,那么就会走D1.的情况1.。

当通过POST来访问"/oauth2/authorize"时(即在确认授权页面点击确认按钮),会设置为consent = true,会走2.当前请求确认授权,会创建一个已认证的AuthenticationToken,同时设置consentRequired = false,那么就会走D1.的情况2.。

D4.OAuth2ClientAuthenticationFilter

进行客户端认证,这个过滤器会拦截[pattern='/oauth2/token', POST]、[pattern='/oauth2/introspect', POST]、[pattern='/oauth2/revoke', POST]。

根据当前请求的入参等属性,判断使用不同客户端授权方法的provider拿到Authentication。

拿到之后进行存储到SecurityContextHolder里面,会继续调用下一个过滤器。

D5.ClientSecretAuthenticationProvider

由于我们接口发起的是基于Auth头部模式,所以会通过这个provider拿到Authentication。

D6.OAuth2TokenEndpointFilter

这个就很熟悉了,是获取到token。他也是根据当前的request属性在不同converter里面进行判断,直到拿到Authentication,这里只要请求接口按照授权码模式的请求规定,提供参数,就能转换成未认证的OAuth2AuthorizationCodeAuthenticationToken。

(授权码模式是这个类型的token,我们自定义的密码模式是OAuth2ResourceOwnerPasswordAuthenticationToken,由此可见这里可能拿到不同类型的token)

然后根据token类型,从多个provider中匹配中对应的,最终拿到Authentication。

D7.OAuth2AuthorizationCodeAuthenticationConverter

根据request里面的"grant_type"、"code"等属性判断,最终转换成未认证的OAuth2AuthorizationCodeAuthenticationToken。

D8.OAuth2AuthorizationCodeAuthenticationProvider

授权码模式的token匹配中的是OAuth2AuthorizationCodeAuthenticationProvider,在这个里面会根据当前客户端的配置,拿到token。

这里面的流程就跟我们自定义密码模式时的OAuth2ResourceOwnerBaseAuthenticationProvider类似~~~

最终使用的token生成器和refreshtoken生成器都是我们自定义的!

2.密码模式中已经添加的

D9.CustomeOAuth2AccessTokenGenerator

C2.处理类

D1.FormAuthenticationFailureHandler

登录失败处理器,实现 AuthenticationFailureHandler接口,重写方法,逻辑是,重定向到"/token/login?error=%s"。

    @Overridepublic void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {log.debug("表单登录失败:{}", exception.getLocalizedMessage());String url = HttpUtil.encodeParams(String.format("/token/login?error=%s", exception.getMessage()),CharsetUtil.CHARSET_UTF_8);response.sendRedirect(url);}

D2.SsoLogoutSuccessHandler

登出成功处理器,实现 LogoutSuccessHandler 接口,重写方法

    private static final String REDIRECT_URL = "redirect_url";@Overridepublic void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication)throws IOException {if (response == null) {return;}// 获取请求参数中是否包含 回调地址String redirectUrl = request.getParameter(REDIRECT_URL);if (StrUtil.isNotBlank(redirectUrl)) {response.sendRedirect(redirectUrl); //注意哦,这里重定向的时候如果是完整的路径会直接重定向,如果不是则会拼接当前系统的路径}else if (StrUtil.isNotBlank(request.getHeader(HttpHeaders.REFERER))) {// 默认跳转referer 地址String referer = request.getHeader(HttpHeaders.REFERER);response.sendRedirect(referer);}}

B3.端点类

 D1.PigTokenEndpoint

我们需要添加三个接口。一个是请求认证页面,使用的是Freemark,由于自定义的登录失败处理器直接返回的是/token/login?error=XXX的格式,所以我们可以直接在登录页面显示失败原因。

一个是请求确认授权页面,这个页面是在过滤器中进行重定向的,而且也会传参数,我们就直接拿到参数。

一个是获取用户信息接口,这个接口是根据accesstoken拿到对应的用户认证信息,一定返回一个用户唯一标识,其余的不可暴露自己系统的用户信息即可。

/*** 认证页面* @param modelAndView* @param error 表单登录失败处理回调的错误信息* @return ModelAndView*/@GetMapping("/login")public ModelAndView loginPage(ModelAndView modelAndView, @RequestParam(required = false) String error) {modelAndView.setViewName("ftl/login");modelAndView.addObject("error", error);return modelAndView;}/*** @Description: 授权确认页面* @param principal* @param modelAndView* @param clientId* @param scope* @param state* @Return: org.springframework.web.servlet.ModelAndView*/@GetMapping("/confirm_access")public ModelAndView confirm(Principal principal, ModelAndView modelAndView,@RequestParam(OAuth2ParameterNames.CLIENT_ID) String clientId,@RequestParam(OAuth2ParameterNames.SCOPE) String scope,@RequestParam(OAuth2ParameterNames.STATE) String state) {RegisteredClient clientDetails = Optional.ofNullable(registeredClientRepository.findByClientId(clientId)).orElseThrow(() -> new OAuth2AuthenticationException("clientId 不合法"));Set<String> authorizedScopes = clientDetails.getScopes();modelAndView.addObject("clientId", clientId);modelAndView.addObject("state", state);modelAndView.addObject("scopeList", authorizedScopes);modelAndView.addObject("principalName", principal.getName());modelAndView.setViewName("ftl/confirm");return modelAndView;}/*** 第三方客户端获取用户信息。必须有用户唯一标识哦!*/@GetMapping("/getUserInfo")public R getUserInfo(String token) {if (!StringUtils.hasText(token)) {return R.failed("Invalid Bearer Token, token missing :" + token);}OAuth2Authorization authorization = this.oAuth2AuthorizationService.findByToken(token, OAuth2TokenType.ACCESS_TOKEN);// 如果令牌不存在 返回401if (authorization == null || authorization.getAccessToken() == null) {return R.failed("Invalid Bearer Token, token invalid :" + token);}Map<String, Object> claims = authorization.getAccessToken().getClaims();return R.ok(claims);}

B5.页面

主要就是下面四个文件。静态文件默认就是/static路径开始。freemarker模板路径是在application里面配置的。

模板文件就不贴出来了,都挺简单的。

9f3b5e69cad24a8492691178b38e4f2f.png

A4.客户端后端代码实现(待实现)


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/46629.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

oauth2授权码模式遇到的坑,1.走网关无法返回授权码 2.refresh_token新token丢失用户信息

主要有2个坑&#xff1a; 1.通过网关访问/oauth/authorize的时候&#xff0c;无法跳转到redirect_uri返回授权码 2.访问/oauth/token 刷新token的时候&#xff0c;新的token解析后用户信息丢失&#xff0c;用户信息变成了用户名 问题一 通过网关访问/oauth/authorize的时候&…

微信开发者工具代码仓的管理以及错误:Push failed Error: invalid authentication scheme 的解决办法,微信小程序代码仓项目删除

文章目录 1、新建代码仓2、报错&#xff1a;Push failed Error: invalid authentication scheme 的解决办法3、代码仓项目的删除 1、新建代码仓 如果你的代码已经存在&#xff0c;单还是没有推送过&#xff0c;可先从以下位置进入到微信的GitHub仓库新建一个仓库。 新建一个…

微信授权登录返回code无效,签名不一致

在项目中&#xff0c;微信授权登录偶尔出现code无效&#xff0c;签名不一致的返回提示&#xff0c;但是就是偶然性 原因&#xff1a;没有调用wx.login()就获取用户的encryptedData、signature等信息 先看下官方的登录流程 很多开发者会把 login 和 getUserInfo 捆绑调用当成登…

Android集成网易云IM实现单聊和群聊

本人一直对即时通信感兴趣,但是我的日常工作与这个没有任何接触,所以一直没有时间去看,去研究,刚好最近有点空闲时间,便乘机去学习了一些皮毛 废话和客套话也不多说,先看效果 界面做的比较丑,大家不要在意哦,主要是实现功能 首页(登录网易云信IM) 单聊 群聊

从ChatGPT聊天服务上深挖Http、WebScoket和SSE推送技术的区别

简述 —— 三种方式进行通讯 回忆TCP/IP协议 其中&#xff0c;三次握手过程的步骤为&#xff1a; 客户端向服务端发送 SYN 报文&#xff0c;其中 seq 表示客户端的初始序列号。服务端收到 SYN 报文后&#xff0c;向客户端发送 SYNACK 报文&#xff0c;其中 ack 表示服务端收到…

Android腾讯云通信集成记录(快速实现聊天功能)

利用小假期的时间学习了一下怎么使用腾讯云通信&#xff0c;过程中的坑还是比较多&#xff0c;写篇博客记录一下集成过程方便自己以后查看。 先看下效果图&#xff1a; 首先需要去腾讯云通信的官网去登录&#xff0c;并且创建一个云通信应用。 免费的就好&#xff0c;腾讯云…

技术干货 | 网易云信大规模聊天室系统架构解析

导读&#xff1a;聊天室是一类非常重要的 IM 系统&#xff0c;不同于单聊和群聊&#xff0c;聊天室是一种大规模的实时消息分发系统。本文我们来详细介绍一下网易云信大规模聊天室系统的具体架构以及实践应用案例。 文&#xff5c;曹佳俊 网易云信资深服务端开发工程师 聊天室有…

2022倒带-NutUI

前言 时光飞逝&#xff0c;流年似水&#xff0c;让我们倒带 2022&#xff0c;回首这跌宕起伏一年走过的 “升级之路”。 NutUI 表现如何&#xff1f; 成绩单等着您打分&#xff01; 2022 是 NutUI 在技术长廊中探索和成长的第四个年头&#xff0c;悄然度过了自己的“孩提“时…

chatgpt赋能python:Python量化开发:实现金融市场的策略化投资

Python量化开发&#xff1a;实现金融市场的策略化投资 随着金融市场竞争的日益激烈&#xff0c;传统的以人工分析及定量研究为主的投资方法已经无法满足投资者的需求。因此&#xff0c;数字化投资逐渐成为趋势&#xff0c;采用量化投资来实现金融市场的策略化投资已经成为了投…

英语基础:四级431、六级没过,考研英语78分经验分享 (23考研)

前言 博主备考时的英语基础&#xff1a;英语四级431分, 六级没过 研究生英语二分数&#xff1a;78分 ps&#xff1a;也许这个分数段不是一些大佬的目标分数 (80)&#xff0c;本篇文章的目的只是帮助一些英语基础差的同学&#xff0c;提供一些备考经验。 一、基础阶段 时间&…

考研复试_英语面试_必备的30个问题

前言&#xff1a; 本人英语口语处于那种的“哑巴式”&#xff0c;没有准备就根本不知道扯啥。于是整理了一些出现频率最高的英语问题&#xff08;回答对我而言两三句简单句就撑死了&#xff0c;因没有参考价值大家就还是自己准备回答&#xff09;&#xff0c;以防到时候陷入迷之…

国外问卷调查有哪几种类型?

大家好&#xff0c;我是小飞匠&#xff0c;今天来聊聊国外问卷调查分为哪几种&#xff1f;新手适合做哪种&#xff1f;国外问卷调查项目最近一直很火&#xff0c;咨询的人也比较多。因为国外问卷调查其实分为很多种类。通过不同的展现方式、付款方式&#xff0c;我们称为不同的…

什么是海外调查问卷?都有哪些形式

国内外有很多正规的公司都是做这个行业的。为什么会有这么多公司在做这个行业&#xff1f;主要是由于市场上问卷调查需求较多决定的&#xff0c;很多商家在进行新产品研发前的需求以及产品销售后的一些满意度征集时&#xff0c;往往会通过这种问卷调查公司收集特定人群的意见与…

国外问卷调查好做吗?为大家分享干货!

随着5G的出现&#xff0c;2021可谓是真正的进入到了互联网时代。互联网时代的出现更大的为人们提供了更多的机会&#xff0c;让人们的工作以及生活都得到了更大的便利。也使人们的创业的机会变得更多了起来。 在互联网的背景下&#xff0c;足不出户的进行互联网的创业的人们多了…

开源的问卷调查系统

一、项目简介 今天推荐一款基于 JAVA WEB 的开源问卷表单系统。 二、实现功能 支持创建问卷 支持设计问卷 支持基础题型 支持收集卷子 支持默认统计 支持原始答卷 三、技术选型 后台开发&#xff1a;Java、SpringBoot【企业版】&#xff0c;Struts2【开源版】 前端开…

海外调查问卷中的站点查

国内外很多正规公司都在从事这个行业。为什么有这么多公司在做这个行业&#xff1f;主要是由于市场上对问卷调查的需求量很大。很多商家在收集新产品研发前的需求和产品销售后的一些满意度时&#xff0c;往往会通过这种问卷调查公司来收集特定人群的意见和想法。这些问卷调查公…

微信端问卷调查

上周&#xff0c;股份公司突然需要问卷调查功能&#xff0c;要的很着急。和一同事用了大约两天时间&#xff0c;将该功能完成。 项目情况是&#xff1a;要求在PC、微信端可以填写内容&#xff1b;微信端根据二维码打开问卷页面。 UI框架&#xff1a; bootstrap 效果如下&…

微信小程序-调查问卷

调查问卷 搭建本地服务器新建小程序注意 搭建本地服务器 使用node.js&#xff08;要安装node.js&#xff09;搭建本地HTTP服务器&#xff0c;在小程序所在目录创建web目录 &#xff0c;并在web目录下打开CMD&#xff0c;初始化项目 npm init -y然后安装express框架 npm inst…

在当前就业形势下,如何提高应届生在职场中的竞争力,让ChatGPT帮你解答

在当前就业形势下&#xff0c;应届生要提高自身的竞争力&#xff0c;可以从以下几个方面入手&#xff1a; 学习技能&#xff1a;不断学习提升自身专业和技能水平&#xff0c;获取行业认证证书&#xff0c;参加培训课程。 实习和项目实践&#xff1a;通过实践课程&#xff0c;公…

泰裤辣!ChatGPT帮你制定个人发展计划,助力你实现职场腾飞……

作为一名职场人&#xff0c;你是否曾经感到自己的职业生涯缺乏方向和规划&#xff1f; 是否曾经为了应对工作中的挑战而感到力不从心&#xff1f; 如果你的答案是肯定的&#xff0c;那么认真制定自己的个人发展计划&#xff0c;为自己的职业生涯打下坚实的基础就是你必须要做…