Springbootåå端å离-æ´åShiro-Md5å å¯ä¸è®¤è¯ç»å½ââå¦ä¹ è®°å½
åèåé´è¿éï¼https://blog.csdn.net/bbxylqf126com/article/details/110501155
https://blog.csdn.net/weixin_42375707/article/details/111145907
https://blog.csdn.net/qq_34845394/article/details/94858168
æ¬äººï¼è¿·è«å¤§èªæ-å¼ ç«¥å¦ã
ç®åå¨å¦shiroï¼ä¹åå¦è¿springsecurityãç½ä¸è¯´åè æ¯åè 容æï¼æåè§å¾æ¯ä¸æ¯ï¼ï¼ï¼é¾éæ¯æï¼ï¼ï¼ï¼
æç« ç®å½
- Springbootåå端å离-æ´åShiro-Md5å å¯ä¸è®¤è¯ç»å½ââå¦ä¹ è®°å½
-
- ä¾èµ
- ç´æ¥ä¸ä»£ç ï¼æè®¤ä¸ºå ³é®ï¼
- æ¥ä¸æ¥æ¯âåå å¼â
- åå¨åé¢
ä¾èµ
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.0</version>
</dependency>
<!-- SpringBootText注解ä¾èµ -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- Junitä¾èµ -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<!--çé¨ç½²-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<!--shiro-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-starter</artifactId>
<version>1.5.3</version>
</dependency>
<!--æ¥å¿-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<!-- <dependency>-->
<!-- <groupId>org.slf4j</groupId>-->
<!-- <artifactId>slf4j-log4j12</artifactId>-->
<!-- <version>1.7.21</version>-->
<!-- </dependency>-->
<!-- <dependency>-->
<!-- <groupId>org.slf4j</groupId>-->
<!-- <artifactId>jcl-over-slf4j</artifactId>-->
<!-- <version>1.7.21</version>-->
<!-- </dependency>-->
<!-- <dependency>-->
<!-- <groupId>commons-logging</groupId>-->
<!-- <artifactId>commons-logging</artifactId>-->
<!-- <version>1.1.3</version>-->
<!-- </dependency>-->
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- mybatis-plus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.0.5</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.3.2</version>
</dependency>
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.0</version>
</dependency>
<!-- Swagger -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.7.0</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.7.0</version>
</dependency>
</dependencies>
ç´æ¥ä¸ä»£ç ï¼æè®¤ä¸ºå ³é®ï¼
shiroæ ¸å¿é ç½®
package com.ztxue.mybatis_plus.shiro;
import com.ztxue.mybatis_plus.utils.ShiroConstant;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* Shiroçæ ¸å¿é
置类ï¼ç¨æ¥æ´åshiroæ¡æ¶
*/
@Configuration
public class ShiroConfiguration {
//1.å¼å
¥èªå®ä¹realm-CustomerRealm()
@Bean
public Realm getRealm() {
ShiroRealm shiroRealm = new ShiroRealm();
// 设置å¯ç å¹é
å¨
HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
// 设置å å¯æ¹å¼
credentialsMatcher.setHashAlgorithmName(ShiroConstant.HASH_ALGORITHM_NAME.MD5);
// 设置æ£å次æ°
credentialsMatcher.setHashIterations(ShiroConstant.HASH_ITERATORS);
shiroRealm.setCredentialsMatcher(credentialsMatcher);
return shiroRealm;
}
//2.å建å®å
¨ç®¡çå¨
@Bean
public DefaultWebSecurityManager getDefaultWebSecurityManager(Realm realm) {
DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
//å
³èrealm
defaultWebSecurityManager.setRealm(realm);
return defaultWebSecurityManager;
}
//3.å建è¿æ»¤å·¥å-è´è´£æ¦æªææ请æ±
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(DefaultWebSecurityManager defaultWebSecurityManager) {
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
//设置å®å
¨ç®¡çå¨
bean.setSecurityManager(defaultWebSecurityManager);
//è¿æ»¤å¨é¾æ å°
Map<String, String> filter = new LinkedHashMap<>();
/*
* 常ç¨è¿æ»¤å¨å¦ä¸
* anonï¼æ é认è¯è®¿é®
* authcï¼å¿
须认è¯äºæè½è®¿é®
* userï¼è®°ä½æå¼å¯æå¯ä»¥è®¿é®
* permsï¼æ¥æ对æ个èµæºçæéæè½è®¿é®
* */
filter.put("/user/login","anon");
filter.put("/user/regis","anon");
// é
ç½®ä¸ä¼è¢«æ¦æªçé¾æ¥ 顺åºå¤æï¼å¿
é¡»é
ç½®å°æ¯ä¸ªéæç®å½
filter.put("/swagger-ui.html/**", "anon");
filter.put("/webjars/**","anon");
// ææurlæ¦æª
// filter.put("/**", "authc");
// é
ç½®éåº è¿æ»¤å¨,å
¶ä¸çå
·ä½çéåºä»£ç Shiroå·²ç»æ¿æ们å®ç°äº, ä½ç½®æ¾å¨ anonãauthcä¸é¢
filter.put("/user/logout", "logout");
// ä¿®æ¹shiroé»è®¤ç»å½å°åï¼ç»å½æåä¹åè¿åç¨æ·åºæ¬ä¿¡æ¯åtokenç»å端
bean.setLoginUrl("/user/login");
// 设置æåä¹åè¦è·³è½¬çé¾æ¥
bean.setSuccessUrl("/user/regis");
// æ¦æªæªææè·¯å¾
bean.setUnauthorizedUrl("/user/unauthorized");
//è¿æ»¤å¨é¾ä¼ å¼
bean.setFilterChainDefinitionMap(filter);
return bean;
}
}
èªå®ä¹çrealm
package com.ztxue.mybatis_plus.shiro;
import com.ztxue.mybatis_plus.fv_sys.service.user.FvUserService;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.List;
/**
* èªå®ä¹realm--æ¾å¼ä½¿ç¨.iniæ件ï¼ä½¿ç¨æ°æ®åºæ¥è¯¢
*/
public class ShiroRealm extends AuthorizingRealm {
@Autowired
FvUserService userService;
@Autowired
FvRoleService roleService;
@Autowired
FvPermissionService permissionService;
// ææ
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
}
// 认è¯
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
//è·åç¨æ·çè¾å
¥çè´¦å·
String userName = (String) token.getPrincipal();
System.out.println("userName----------------------------->>>" + userName);
//éè¿usernameä»æ°æ®åºä¸æ¥æ¾ User对象.
//å®é
项ç®ä¸ï¼è¿éå¯ä»¥æ ¹æ®å®é
æ
åµåç¼åï¼å¦æä¸åï¼Shiroèªå·±ä¹æ¯ææ¶é´é´éæºå¶ï¼2åéå
ä¸ä¼éå¤æ§è¡è¯¥æ¹æ³
FvUser user = userService.findByName(userName);
System.out.println("user.getUPassword()----------------------------->>>" + user.getUPassword());
System.out.println("userName----------------------------->>>" + userName);
if (!ObjectUtils.isEmpty(user)) {
return new SimpleAuthenticationInfo(
// ä¹å¯ä»¥åç¨æ·å
user,
// ä¼ å
¥çæ¯ä»æ°æ®åºä¸è·åå°çpasswordï¼ç¶ååä¸tokenä¸çpasswordè¿è¡å¯¹æ¯å¹é
user.getUPassword(),
// saltâç¨äºå å¯å¯ç 对æ¯ãè¥ä¸éè¦ï¼åå¯ä»¥è®¾ç½®ä¸ºç©º â â
ByteSource.Util.bytes(user.getUSalt()),
// å½årealmçåå
getName()
);
}
return null;
}
}
注æè¿ä¸ªå°æ¹
new SimpleAuthenticationInfo(
// ä¹å¯ä»¥åç¨æ·å
user,
// ä¼ å
¥çæ¯ä»æ°æ®åºä¸è·åå°çpasswordï¼ç¶ååä¸tokenä¸çpasswordè¿è¡å¯¹æ¯å¹é
user.getUPassword(),
// saltâç¨äºå å¯å¯ç 对æ¯ãè¥ä¸éè¦ï¼åå¯ä»¥è®¾ç½®ä¸ºç©º â â
ByteSource.Util.bytes(user.getUSalt()),
// å½årealmçåå
getName()
)
SimpleAuthenticationInfoéé¢ä¸ä¸ªæå个åæ°ï¼ç¬¬ä¸ä¸ªâ
ByteSource.Util.bytes(user.getUSalt()),
è·åççå¼
éæºççæå·¥å ·ç±»ï¼å¯æ¶èå¦ä¹ ï¼
package com.ztxue.mybatis_plus.utils;
import java.util.Random;
/**
* ç¨æ·éæºççæå·¥å
·ç±»
*/
public class SaltUtil {
/**
* çæsaltçéææ¹æ³
* @param n
* @return
*/
public static String getSalt(int n){
char[] chars = "A[email protected]#$%^&*()".toCharArray();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < n; i++) {
char aChar = chars[new Random().nextInt(chars.length)];
sb.append(aChar);
}
return sb.toString();
}
}
MD5å å¯è¯´æç±»ï¼å®ä¹æ常éèå·²ï¼ææè§duckä¸å¿ ï¼å¯è½æ¯ææ°´å¹³ä¸å¤ï¼
package com.ztxue.mybatis_plus.utils;
public class ShiroConstant {
/** éæºççä½æ° **/
public static final int SALT_LENGTH = 8;
/** hashçæ£åæ¬¡æ° **/
public static final int HASH_ITERATORS = 1024;
/** å å¯æ¹å¼ **/
public interface HASH_ALGORITHM_NAME {
String MD5 = "MD5";
}
}
æ¥ä¸æ¥æ¯âåå å¼â
controllerå ³é®ä»£ç
package com.ztxue.mybatis_plus.fv_sys.controller.user;
import com.ztxue.mybatis_plus.config.exception.LoginException;
import com.ztxue.mybatis_plus.fv_sys.entity.user.FvUser;
import com.ztxue.mybatis_plus.fv_sys.mapper.user.UserMapper;
import com.ztxue.mybatis_plus.fv_sys.service.user.FvUserService;
import com.ztxue.mybatis_plus.result.AjaxResult;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* <p>
* å端æ§å¶å¨
* </p>
*
* @author å¼ ç«¥å¦
* @since 2021-07-17
*/
@RestController
@RequestMapping("/user")
@Api(description = "ç¨æ·é¡µ")
public class FvUserController {
@Autowired
FvUserService fvUserService;
@Autowired
UserMapper userMapper;
@ApiOperation("注å")
@PostMapping("/regis")
public AjaxResult register(FvUser user) {
try {
fvUserService.register(user);
return AjaxResult.success("注åæåï¼",user);
} catch (Exception e) {
e.printStackTrace();
return AjaxResult.error("注å失败ï¼å¤§ä¾ 请ä»å¤´åæ¥ï¼");
}
}
@ApiOperation("ç»å½")
@RequestMapping("/login")
public AjaxResult login(String userName, String password) {
// è·åSubjectå®ä¾å¯¹è±¡ï¼ç¨æ·å®ä¾
Subject currentUser = SecurityUtils.getSubject();
// å°ç¨æ·ååå¯ç å°è£
å°UsernamePasswordToken
UsernamePasswordToken token = new UsernamePasswordToken(userName, password);
System.out.println("token============>" + token);
try {
// ä¼ å° MyShiroRealm ç±»ä¸çæ¹æ³è¿è¡è®¤è¯
currentUser.login(token);
return AjaxResult.success(token);
} catch (UnknownAccountException e) {
throw new LoginException("è´¦å·ä¸åå¨!", e);
} catch (IncorrectCredentialsException e) {
throw new LoginException("å¯ç ä¸æ£ç¡®!", e);
} catch (AuthenticationException e) {
throw new LoginException("ç¨æ·éªè¯å¤±è´¥!", e);
}
// ç»å½æåè¿åç¨æ·ä¿¡æ¯
}
}
è¿éç¨ä¸äºå°è£ çç»æé
package com.ztxue.mybatis_plus.result;
import com.ztxue.mybatis_plus.utils.HttpStatus;
import com.ztxue.mybatis_plus.utils.StringUtils;
import java.util.HashMap;
/**
* æä½æ¶æ¯æé
* */
public class AjaxResult extends HashMap<String, Object> {
private static final long serialVersionUID = 1L;
/**
* ç¶æç
*/
public static final String CODE_TAG = "code";
/**
* è¿åå
容
*/
public static final String MSG_TAG = "msg";
/**
* æ°æ®å¯¹è±¡
*/
public static final String DATA_TAG = "data";
/**
* åå§åä¸ä¸ªæ°å建ç AjaxResult 对象ï¼ä½¿å
¶è¡¨ç¤ºä¸ä¸ªç©ºæ¶æ¯ã
*/
public AjaxResult() {
}
/**
* åå§åä¸ä¸ªæ°å建ç AjaxResult 对象
*
* @param code ç¶æç
* @param msg è¿åå
容
*/
public AjaxResult(int code, String msg) {
super.put(CODE_TAG, code);
super.put(MSG_TAG, msg);
}
/**
* åå§åä¸ä¸ªæ°å建ç AjaxResult 对象
*
* @param code ç¶æç
* @param msg è¿åå
容
* @param data æ°æ®å¯¹è±¡
*/
public AjaxResult(int code, String msg, Object data) {
super.put(CODE_TAG, code);
super.put(MSG_TAG, msg);
if (StringUtils.isNotNull(data)) {
super.put(DATA_TAG, data);
}
}
/**
* è¿åæåæ¶æ¯
*
* @return æåæ¶æ¯
*/
public static AjaxResult success() {
return AjaxResult.success("æä½æå");
}
/**
* è¿åæåæ°æ®
*
* @return æåæ¶æ¯
*/
public static AjaxResult success(Object data) {
return AjaxResult.success("æä½æå", data);
}
/**
* è¿åæåæ¶æ¯
*
* @param msg è¿åå
容
* @return æåæ¶æ¯
*/
public static AjaxResult success(String msg) {
return AjaxResult.success(msg, null);
}
/**
* è¿åæåæ¶æ¯
*
* @param msg è¿åå
容
* @param data æ°æ®å¯¹è±¡
* @return æåæ¶æ¯
*/
public static AjaxResult success(String msg, Object data) {
return new AjaxResult(HttpStatus.SUCCESS, msg, data);
}
/**
* è¿åé误æ¶æ¯
*
* @return
*/
public static AjaxResult error() {
return AjaxResult.error("æä½å¤±è´¥");
}
/**
* è¿åé误æ¶æ¯
*
* @param msg è¿åå
容
* @return è¦åæ¶æ¯
*/
public static AjaxResult error(String msg) {
return AjaxResult.error(msg, null);
}
/**
* è¿åé误æ¶æ¯
*
* @param msg è¿åå
容
* @param data æ°æ®å¯¹è±¡
* @return è¦åæ¶æ¯
*/
public static AjaxResult error(String msg, Object data) {
return new AjaxResult(HttpStatus.ERROR, msg, data);
}
/**
* è¿åé误æ¶æ¯
*
* @param code ç¶æç
* @param msg è¿åå
容
* @return è¦åæ¶æ¯
*/
public static AjaxResult error(int code, String msg) {
return new AjaxResult(code, msg, null);
}
}
serviceImpl é¨å
@Autowired
UserMapper userMapper;
@Override
public FvUser findByName(String name) {
return userMapper.findByName(name);
}
// 注å
@Override
public void register(FvUser user) {
// çæéæºç
String salt = SaltUtil.getSalt(ShiroConstant.SALT_LENGTH);
// ä¿åéæºç
user.setUSalt(salt);
// çæå¯ç
Md5Hash password = new Md5Hash(user.getUPassword(), salt, ShiroConstant.HASH_ITERATORS);
// ä¿åå¯ç
user.setUPassword(password.toHex());
userMapper.insert(user);
System.out.println("çæçç------------------>"+salt);
System.out.println("MD5å å¯çå¯ç ------------------>"+password);
}
serivce é¨å
FvUser findByName(String name);
// 注å
void register(FvUser user);
mapper
package com.ztxue.mybatis_plus.fv_sys.mapper.user;
import com.ztxue.mybatis_plus.fv_sys.entity.user.FvUser;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
/**
* <p>
* Mapper æ¥å£
* </p>
*
* @author å¼ ç«¥å¦
* @since 2021-07-17
*/
@Mapper
public interface UserMapper extends BaseMapper<FvUser> {
@Select("select * from fv_user where u_name = #{uName}")
FvUser findByName(String name);
@Insert("INSERT INTO fv_user ( u_name, u_password, is_deleted, gmt_create, gmt_modified, u_salt ) VALUES (#{uName},#{uPassword},#{isDeleted},#{gmtCreate},#{gmtModified},#{uSalt})")
int add(FvUser user);
}
å ¶å®mapper æç¨äºmybatis_plus ï¼ç»§æ¿äºå®çåºæ¬mapperï¼å¾å¤æ¹æ³å¯ä»¥ä¸ç¨åï¼ä½æè¿ä¸å¤ªçã
ç¶åè¿æ¯æçå®ä½ç±»åæ°æ®åº
package com.ztxue.mybatis_plus.fv_sys.entity.user;
import com.baomidou.mybatisplus.annotation.*;
import java.util.Date;
import java.io.Serializable;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* <p>
*
* </p>
*
* @author å¼ ç«¥å¦
* @since 2021-07-17
*/
@Data
@EqualsAndHashCode(callSuper = false)
public class FvUser implements Serializable {
private static final long serialVersionUID = 1L;
/**
* ç¨æ·id
*/
@TableId(value = "u_id", type = IdType.AUTO)
private Integer uId;
/**
* ç¨æ·å
*/
private String uName;
/**
* ç¨æ·ææº
*/
private String uPhone;
/**
* é®ç®±
*/
public String uEmail;
/**
* å¯ç
*/
public String uPassword;
/**
* é»è¾å é¤
*/
@TableLogic
@TableField(fill = FieldFill.INSERT)
private Integer isDeleted;
/**
* å建æ¶é´
*/
@TableField(fill = FieldFill.INSERT)
private Date gmtCreate;
/**
* ä¿®æ¹æ¶é´
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date gmtModified;
/**
* å¯ç ç. éæ°å¯¹çéæ°è¿è¡äºå®ä¹ï¼ç¨æ·å+saltï¼è¿æ ·å°±ä¸å®¹æè¢«ç ´è§£ï¼å¯ä»¥éç¨å¤ç§æ¹å¼å®ä¹å ç
*/
@TableField(value = "u_salt")
private String uSalt;
}
CREATE TABLE `fv_user` (
`u_id` bigint NOT NULL AUTO_INCREMENT COMMENT 'ç¨æ·id',
`u_name` varchar(255) DEFAULT NULL COMMENT 'ç¨æ·å',
`u_phone` varchar(255) DEFAULT NULL COMMENT 'ç¨æ·ææº',
`u_email` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT 'é®ç®±',
`u_password` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT 'å¯ç ',
`u_salt` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT 'ç',
`u_state` int NOT NULL DEFAULT '1' COMMENT 'ç¶æ',
`is_deleted` tinyint NOT NULL DEFAULT '0' COMMENT 'é»è¾å é¤',
`gmt_create` datetime DEFAULT NULL COMMENT 'å建æ¶é´',
`gmt_modified` datetime DEFAULT NULL COMMENT 'ä¿®æ¹æ¶é´',
PRIMARY KEY (`u_id`)
) ENGINE=InnoDB AUTO_INCREMENT=24 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
忽ç¥é¨ååç¼åé¨åæ ç¨å段
åå¨åé¢
å主ç®åå¨å¦shiroï¼å 为å¨æåå®ä¹ ï¼ç¶åå®ä¹ ä¼ä¸ç¨çæ¯shiroä¸æ¯springSecurityï¼èä¸ä¼ä¸åå端å离ï¼æå好对åå端å离ä¸çæï¼å°±è¶çä¸åå¦äºï¼ä½æ¯ç¢°ä¸è®¸å¤å°é¾ã
åå端å离éè¦åAPIï¼æµè¯APIæç¨swaggerï¼ç°å¦ç°ç¨å°±å¾niceï¼æ¡æ¶åAPIPostè¿ä¸ªè½¯ä»¶ï¼Postmanæå®å¨ç¨ä¸æ¯ï¼ã
shiroç®åå¦åºæ¬ç认è¯+MD5å å¯å°±æå¾æ头æè涨ï¼å 为å主æ¬äººâ大èªæâï¼è¿æ»æ¯å¥½é«éªè¿ã
MD5å å¯é»è¾ä¸å¤æï¼å°±âç¨æ·æ³¨åâæ¶å å¯åå ¥æ°æ®åºï¼âç¨æ·è®¤è¯âæ¶ä»æ°æ®åºååºæ¥çå å¯å¯ç ï¼shiroè½è§£æï¼ä¸å°±é ±ç´«ï¼
åé¢ä¹ä¼æshiroçå¦ä¹ è®°å½ï¼è¿æ¯vxï¼handsomeztxãå¸ææå°ä¼ä¼´ä¸åå¦ä¹ 交æµã大佬æ¹è¯ææã