SpringBoot项目搭建 + Jwt登录
临时接了一个小项目,有需要搭一个小项目,简单记录一下项目搭建过程以及整合登录功能。
1.首先拿到的是一个码云地址,里面是一个空的文件夹,只有一个

2. 拿到HTTPS码云项目地址链接,在IDEA中clone输入项目地址

3. 在项目根目录文件夹下右击,选择Add Frameworks Support, 添加maven框架支持

4. 然后得到pom.xml文件, 在pom文件中配置springboot
点击查看代码
<groupId>groupId</groupId>
<artifactId>resource-manager-background</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<artifactId>spring-boot-starter-parent</artifactId>
<groupId>org.springframework.boot</groupId>
<version>2.3.12.RELEASE</version>
</parent>
5. 引入其他相关依赖 mysql druid mybatisplus
点击查看代码
<dependencies>
<!-- mysqlconnect -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.1</version>
</dependency>
<!-- Mybatis-plus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.0</version>
</dependency>
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.2.12</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.4.0</version>
</dependency>
<!-- jwt -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
</dependency>
<!-- security -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- springweb -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<swagger2.version>2.9.2</swagger2.version>
</properties>
6. 创建security文件夹,创建Security核心配置类
点击查看代码
package com.gameresource.security.config;
import com.gameresource.base.bean.Resp;
import com.gameresource.base.constant.RespConstant;
import com.gameresource.security.filter.JwtAuthTokenFilter;
import com.gameresource.security.util.RespUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private IgnoreUrlsConfig ignoreUrlsConfig;
@Override
protected void configure(HttpSecurity http) throws Exception {
ExpressionUrlAuthorizationConfigurer<HttpSecurity>.ExpressionInterceptUrlRegistry registry = http.authorizeRequests();
for (String url : ignoreUrlsConfig.getUrls()) {
registry.antMatchers(url).permitAll();
}
registry.antMatchers(HttpMethod.OPTIONS).permitAll();
registry.and()
.authorizeRequests()
.anyRequest()
.authenticated()
//关闭跨站请求防护及不使用防护
.and()
.csrf()
.disable()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
//自定义权限拒绝处理类
.and()
.exceptionHandling()
.accessDeniedHandler(((request, response, accessDeniedException) -> {
RespUtils.outJson(response, Resp.code(RespConstant.NOT_PERMISSION));
}))
.authenticationEntryPoint((request, response, authenticationException) -> {
RespUtils.outJson(response, Resp.code(RespConstant.NOT_AUTHORIZE));
})
//自定义权限拦截器JWT过滤器
.and()
.addFilterBefore(jwtAuthTokenFilter(), UsernamePasswordAuthenticationFilter.class);
//关闭默认登录
http.formLogin().disable().httpBasic().disable();
}
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers(
"/auth/login",
"/doc.html",
"/swagger-ui.html",
"/v2/api-docs",
"/swagger-resources/**",
"/**/*.js",
"/**/*.css",
"/**/*.png",
"/**/*.ico"
);
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public JwtAuthTokenFilter jwtAuthTokenFilter() {
return new JwtAuthTokenFilter();
}
}
7. 自定义JWT过滤器
点击查看代码
package com.gameresource.security.filter;
import cn.hutool.core.util.StrUtil;
import com.gameresource.base.bean.Resp;
import com.gameresource.base.constant.RespConstant;
import com.gameresource.security.constant.SecurityConstant;
import com.gameresource.security.config.IgnoreUrlsConfig;
import com.gameresource.security.util.JwtTokenUtil;
import com.gameresource.security.util.RespUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* 自定义JWT过滤器
*/
@Slf4j
public class JwtAuthTokenFilter extends OncePerRequestFilter {
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private IgnoreUrlsConfig ignoreUrlsConfig;
@Autowired
private JwtTokenUtil jwtTokenUtil;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {
String authHeader = request.getHeader(SecurityConstant.TOKEN_HEADER);
String uri = request.getRequestURI();
if (StrUtil.startWith(authHeader, SecurityConstant.TOKEN_HEAD)) {
try {
String token = StrUtil.trim(authHeader.substring(SecurityConstant.TOKEN_HEAD.length()));
String username = jwtTokenUtil.getUserNameFromToken(token);
log.info("checking username:{}", username);
if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
if (jwtTokenUtil.validateToken(token)) {
UserDetails userDetails = userDetailsService.loadUserByUsername(username);
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
log.info("pass user: {}", username);
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
}
}
} catch (BadCredentialsException exception) {
RespUtils.outJson(response, Resp.fail(exception.getMessage()));
return;
} catch (Exception exception) {
log.error("Spring Security 校验异常: ", exception);
RespUtils.outJson(response, Resp.code(RespConstant.NOT_AUTHORIZE));
return;
}
}
chain.doFilter(request, response);
}
}
8. 创建JWT工具类
点击查看代码
package com.gameresource.security.util;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.gameresource.security.constant.SecurityConstant;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
* jwt工具类
*/
@Slf4j
@Component
@RequiredArgsConstructor
public class JwtTokenUtil {
@Value("${project.name}")
private String projectName;
@Value("${Jwt.secret}")
private String secret;
@Value("${Jwt.expiration}")
private Integer expiration;
private final StringRedisTemplate redisTemplate;
public boolean validateToken(String token) {
String username = getUserNameFromToken(token);
String redisKey = TokenRedisKeyUtils.installAuthToken(projectName, username);
if (Boolean.FALSE.equals(redisTemplate.hasKey(redisKey))) {
throw new BadCredentialsException("Token不存在或已过期");
}
String redisToken = redisTemplate.opsForValue().get(redisKey);
if (!StrUtil.equals(redisToken, token)) {
throw new BadCredentialsException("无效token");
}
return true;
}
/**
* 从token中获取用户名
*/
public String getUserNameFromToken(String token) {
return Convert.toStr(getValueFromToken(token, SecurityConstant.CLAIM_KEY_USERNAME));
}
private Object getValueFromToken(String token, String key) {
Claims claim = getClaimsFromToken(token);
return claim != null ? claim.get(key) : null;
}
private Claims getClaimsFromToken(String token) {
Claims claims = null;
try {
claims = Jwts.parser()
.setSigningKey(secret)
.parseClaimsJws(token)
.getBody();
} catch (Exception exception) {
log.error("jwt格式验证失败: {}", token);
}
return claims;
}
public String generateToken(UserDetails userDetails) {
return generateToken(userDetails, null);
}
private String generateToken(UserDetails userDetails, Map<String, Object> claims) {
if (claims == null) {
claims = new HashMap<>();
}
claims.put(SecurityConstant.CLAIM_KEY_USERNAME, userDetails.getUsername());
int ttl = expiration * 1000;
DateTime dateTime = DateUtil.offsetMillisecond(new Date(), ttl);
String token = Jwts.builder()
.setClaims(claims)
.setExpiration(dateTime)
.signWith(SignatureAlgorithm.HS256, secret)
.compact();
String redisKey = TokenRedisKeyUtils.installAuthToken(projectName, userDetails.getUsername());
redisTemplate.opsForValue().set(redisKey, token, ttl, TimeUnit.MILLISECONDS);
return token;
}
public Boolean removeToken(String username) {
return redisTemplate.delete(TokenRedisKeyUtils.installAuthToken(projectName, username));
}
}
9. 响应工具类
点击查看代码
package com.gameresource.security.util;
import com.alibaba.fastjson.JSON;
import com.gameresource.base.bean.Resp;
import org.springframework.http.MediaType;
import org.springframework.security.core.parameters.P;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
public class RespUtils {
public static void outJson(HttpServletResponse response, Resp resp) {
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
response.setStatus(HttpServletResponse.SC_OK);
response.setCharacterEncoding("utf-8");
try (PrintWriter printWriter = response.getWriter();) {
printWriter.print(JSON.toJSONString(resp));
printWriter.flush();
}catch (IOException exception) {
throw new RuntimeException();
}
}
}
10. UserDetailsService的自定义
点击查看代码
package com.gameresource.security.service;
import com.gameresource.manage.sys.SysUserManager;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
@Service
@RequiredArgsConstructor
public class AuthUserDetailService implements UserDetailsService {
private final SysUserManager userManager;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return userManager.findUserByName(username);
}
}
11. 各种类的实现代码
1. SysUserManager
点击查看代码
package com.gameresource.manage.sys;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.gameresource.module.sys.domain.AuthUser;
import com.gameresource.module.sys.entity.SysUserEntity;
import com.gameresource.module.sys.entity.SysUserRoleEntity;
import com.gameresource.module.sys.service.ISysUserRoleService;
import com.gameresource.module.sys.service.ISysUserService;
import com.gameresource.security.constant.SecurityConstant;
import com.gameresource.security.util.JwtTokenUtil;
import com.sun.org.apache.xpath.internal.operations.Bool;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.stream.Collectors;
@RequiredArgsConstructor
@Component
@Slf4j
public class SysUserManager {
private final ISysUserService userService;
@Autowired
private PasswordEncoder passwordEncoder;
private final ISysUserRoleService userRoleService;
private final JwtTokenUtil jwtTokenUtil;
public String login(String username, String password) {
UserDetails userDetails = findUserByName(username);
if (passwordEncoder.matches(password, userDetails.getPassword())) {
throw new BadCredentialsException("用户名或密码错误");
}
UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(authenticationToken);
return jwtTokenUtil.generateToken(userDetails);
}
public UserDetails findUserByName(String username) {
SysUserEntity userEntity = userService.getByUsername(username);
if (ObjectUtil.isNull(userEntity)) {
throw new UsernameNotFoundException("用户不存在!");
}
if (!userEntity.getStatus()) {
throw new RuntimeException("此账户已被禁用!");
}
List<SysUserRoleEntity> roleEntityList = userRoleService.getByUserId(userEntity.getId());
List<Integer> roleList = roleEntityList.stream().map(SysUserRoleEntity::getRoleId).collect(Collectors.toList());
return AuthUser.create(userEntity, roleList, null);
}
public boolean logout(HttpServletRequest request) {
String token = request.getHeader(SecurityConstant.TOKEN_HEADER);
if (StrUtil.isBlank(token)) {
return false;
}
token = token.substring(SecurityConstant.TOKEN_HEAD.length());
String username = jwtTokenUtil.getUserNameFromToken(token);
Boolean logout = jwtTokenUtil.removeToken(username);
if (!logout) {
log.error("============登出失败============");
}
log.info("=======================用户退出{}======================", username);
return true;
}
}
2. AuthUser
点击查看代码
package com.gameresource.module.sys.domain;
import cn.hutool.core.collection.CollUtil;
import com.gameresource.module.sys.entity.SysUserEntity;
import lombok.Data;
import lombok.experimental.Accessors;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
@Data
@Accessors(chain = true)
public class AuthUser implements UserDetails {
/**
* 用户id
*/
private int userId;
/**
* 用户名
*/
private String username;
/**
* 密码
*/
private String password;
/**
* 是否启用
*/
private boolean status;
/**
* 用户角色列表
*/
private List<Integer> roleIds;
/**
* 用户权限列表
*/
private Collection<? extends GrantedAuthority> authorities;
public static UserDetails create(SysUserEntity sysUser, List<Integer> roleIds, List<String> perms) {
if (sysUser == null) {
return null;
}
List<GrantedAuthority> authorities = Collections.emptyList();
if (CollUtil.isNotEmpty(perms)) {
authorities = perms.stream().map(SimpleGrantedAuthority::new).collect(Collectors.toList());
}
return new AuthUser()
.setUserId(sysUser.getId())
.setStatus(sysUser.getStatus())
.setUsername(sysUser.getUsername())
.setPassword(sysUser.getPassword())
.setRoleIds(CollUtil.isNotEmpty(roleIds) ? roleIds : Collections.emptyList())
.setAuthorities(authorities);
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return null;
}
@Override
public String getPassword() {
return password;
}
@Override
public String getUsername() {
return username;
}
@Override
public boolean isAccountNonExpired() {
return false;
}
@Override
public boolean isAccountNonLocked() {
return false;
}
@Override
public boolean isCredentialsNonExpired() {
return false;
}
@Override
public boolean isEnabled() {
return status;
}
}
12. 登录接口
点击查看代码
package com.gameresource.web.sys;
import cn.hutool.core.lang.Assert;
import com.gameresource.base.bean.Resp;
import com.gameresource.manage.sys.SysUserManager;
import com.gameresource.module.sys.domain.AuthUser;
import com.gameresource.module.sys.domain.AuthUserVo;
import com.gameresource.module.sys.domain.LoginReqDto;
import com.gameresource.util.SecurityUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;
@Slf4j
@Api(tags = "登录")
@RestController
@RequestMapping("/auth")
@RequiredArgsConstructor
public class LoginController {
private final SysUserManager sysUserManager;
@ApiOperation("用户登录")
@PostMapping("/login")
public Resp<AuthUserVo> login(@Valid @RequestBody LoginReqDto loginReq) {
try {
log.info("===============loginReq=====================username:[{}],password:[{}]",loginReq.getUsername(), loginReq.getPassword());
String token = sysUserManager.login(loginReq.getUsername(), loginReq.getPassword());
Assert.notBlank(token, "用户名或密码错误");
AuthUser authUser = SecurityUtil.getCurrentUser();
log.info("========================用户{}登录成功!=============================", authUser.getUsername());
return Resp.ok(AuthUserVo.create(authUser, token));
}catch (Exception e) {
log.info("登录失败: ", e);
return Resp.fail(e.getMessage());
}
}
@ApiOperation("用户退出")
@PostMapping("/logout")
public Resp<Boolean> logout(HttpServletRequest request) {
return Resp.result(sysUserManager.logout(request));
}
}
13. 至此结束
其实登录的核心部分就是WebSecurityConfigurerAdapter下的configure方法,我们通过自定义了一个SecurityConfig类来继承了WebSecurityConfigurerAdapter类,并重写配置类的各种选项达到自定义的效果,另外一个就是OncePerRequestFilter来过滤各种请求,在这里我们会处理请求并验证token,这只是项目中比较简单的一种写法,另外security还有很多其他的配置,在这里只是列举了其中的一种较为简单的方式,才疏学浅,还需要多加学习,共勉!
SpringBoot项目搭建 + Jwt登录的更多相关文章
- SpringBoot 项目搭建(详细介绍+案例源码)
SpringBoot 项目搭建 SpringBoot 项目整合源码 SpringBoot 项目整合 一.项目准备 1.1 快速创建 SpringBoot 项目 1.2 标准项目结构图如下 1.3 添加 ...
- SpringBoot之入门教程-SpringBoot项目搭建
SpringBoot大大的简化了Spring的配置,把Spring从配置炼狱中解救出来了,以前天天配置Spring和Mybatis,Springmvc,Hibernate等整合在一起,感觉用起来还是挺 ...
- SpringBoot项目搭建与打包
一.环境准备 本地java环境jdk1.8 Maven版本3.5.2 IDE工具idea2017 二.SpringBoot微服务搭建 1.点击File >> New >> Pr ...
- springboot系列二、springboot项目搭建
一.官网快速构建 1.maven构建项目 1.访问http://start.spring.io/ 2.选择构建工具Maven Project.Spring Boot版本2.1.1以及一些工程基本信息, ...
- 从零开始的SpringBoot项目搭建
前言 今天是我加入博客园的第一天今天刚好学习到SpringBoot,就顺便记录一下吧 一.创建项目 1.创建工程 ① 通过File > New > Project,新建工程,选择Sprin ...
- Springboot项目搭建(3)-shiro登录
shiro简述+实现简单登录:https://www.jianshu.com/p/7f724bec3dc3
- springboot项目搭建及常用技术整合
一.在你建立的工程下创建 Module 选择Spring initializr创建. 二.在Type处选择: Maven Project(项目的构建工具) 三.创建依赖时勾上web,mybatis,m ...
- oa_mvc_easyui_项目搭建及登录页面验证码(1)
1.空项目的搭建,三层的搭建(各层之中的引用) webapp:bll,model,common bll:dal,model dal:model 2.SQL表 ItcastDb:T_UserInfo,T ...
- springboot项目搭建:结构和入门程序
Spring Boot 推荐目录结构 代码层的结构 根目录:com.springboot 1.工程启动类(ApplicationServer.java)置于com.springboot.build包下 ...
随机推荐
- 使用 cookie 的身份验证和授权
前言 在上一章 学学 dotnet core 中的身份验证和授权-1-概念 中,我们大致明白了身份验证和授权两者的关系.那么在本文中,我们将使用 cookie 来做一个简单的身份验证和授权. 本文中我 ...
- .NET C#基础(3):事件 - 不便处理的事就委托出去
0. 文章目的 本文面向有一定.NET C#基础知识的学习者,介绍.NET中事件的相关概念.基本知识及其使用方法 1. 阅读基础 理解C#基本语法(方法的声明.方法的调用.类的定义) 2. 从 ...
- 利用shell脚本自动化备份数据库与手动备份还原数据库操作
1.在linux操作系统上手动备份数据库 mysqldump -h 服务器IP地址 -u root -p数据库密码 --databases 所要备份的数据库名称 > /路径/数据库.sql(自定 ...
- VBA驱动SAP GUI实现办公自动化(一)
小爬之前写过一系列Python驱动SAP GUI实现办公自动化的文章,其实如果我们的实际业务不是太复杂,且我们对VBA语法比较熟悉的话,我们完全可以借助Excel VBA来驱动SAP GUI做很多自动 ...
- hyperlpr centos 使用记录
1.下载最新版python3.7 Anacondawget https://repo.anaconda.com/archive/Anaconda3-5.3.1-Linux-x86_64.sh 2.安装 ...
- tableSizeFor
HashMap tableSizeFor() /** Returns a power of two size for the given target capacity. 1.(不考虑大于最大容量的情 ...
- ElasticSearch7.3学习(三十二)----logstash三大插件(input、filter、output)及其综合示例
1. Logstash输入插件 1.1 input介绍 logstash支持很多数据源,比如说file,http,jdbc,s3等等 图片上面只是一少部分.详情见网址:https://www.elas ...
- 一个bug肝一周...忍不住提了issue
导航 Socket.IO是什么 Socket.IO的应用场景 为什么选socket.io-client-java 实战案例 参考 本文首发于智客工坊-<socket.io客户端向webserve ...
- 浪姐打分看不够?用几行Python代码模拟评委打分
大家好鸭~我是小熊猫比赛大家都看过吧,每次是不是都对比赛成绩充满期待.特别是浪姐的打分看的简直欲罢不能- 今天就用Python来模拟评委打分,这个案例很短也很简单,很适合新手跟小白练习. 在某次十佳歌 ...
- 鹏城杯 WEB_WP
简单的PHP GET /?code=[~%8C%86%8C%8B%9A%92][~%CF]([~%9A%91%9B][~%CF]([~%98%9A%8B%9E%93%93%97%9A%9E%9B%9A ...