要如何做才能拥有自己的网站呢,音乐网站的设计与开发,重庆疾控最新通告今天,织梦手机端网站字体重叠一、 微信小程序登录 (后端实现)
后端的核心任务只有一个 拿 code 换 openid 。
1. 流程详解后端不直接处理微信界面#xff0c;而是提供一个接口给小程序调用。
输入#xff1a;小程序前端传来的临时登录凭证 code。处理#xff1a;后端拿着这个 code appid secret 去找微…一、 微信小程序登录 (后端实现)后端的核心任务只有一个 拿code换openid。1. 流程详解后端不直接处理微信界面而是提供一个接口给小程序调用。输入小程序前端传来的临时登录凭证code。处理后端拿着这个codeappidsecret去找微信服务器核实。输出生成一个我们可以控制的token(JWT) 返回给前端用于后续业务鉴权。2. 代码示例Step 1: 用户JWT令牌校验拦截器/** * jwt令牌校验的拦截器 */ComponentSlf4jpublicclassJwtTokenUserInterceptorimplementsHandlerInterceptor{AutowiredprivateJwtPropertiesjwtProperties;publicbooleanpreHandle(HttpServletRequestrequest,HttpServletResponseresponse,Objecthandler)throwsException{if(!(handlerinstanceofHandlerMethod)){returntrue;}// 1、从请求头中获取令牌Stringtokenrequest.getHeader(jwtProperties.getUserTokenName());// 2、校验令牌try{log.info(jwt校验:{},token);// 这里必须使用 UserSecret (用户端密钥) 进行解密而不是 UserTokenNameClaimsclaimsJwtUtil.parseJWT(jwtProperties.getUserSecretKey(),token);// 这里必须获取 USER_ID而不是 EMP_IDLonguserIdLong.valueOf(claims.get(JwtClaimsConstant.USER_ID).toString());// 3、设置到上下文BaseContext.setCurrentId(userId);log.info(当前用户id{},userId);returntrue;}catch(Exceptionex){log.error(用户端JWT校验失败,ex);// 建议打印异常日志以便调试response.setStatus(401);returnfalse;}}}Step 2: Controller 层 (接收请求)RestControllerRequestMapping(/user/user)Slf4jpublicclassUserController{AutowiredprivateUserServiceuserService;PostMapping(/login)publicResultUserLoginVOwechatLogin(RequestBodyUserLoginDTOuserLoginDO){log.info(微信登录用户数据{},userLoginDO);returnuserService.wechatLogin(userLoginDO);}}Step 3: Service 层 (核心逻辑)这里模拟了通过HttpClient调用微信接口的过程。ServiceSlf4jpublicclassUserServiceImplextendsServiceImplUserMapper,UserimplementsUserService{privatefinalstaticStringWECHAT_LOGIN_URLhttps://api.weixin.qq.com/sns/jscode2session;AutowiredprivateJwtPropertiesjwtProperties;AutowiredprivateWeChatPropertiesweChatProperties;OverridepublicResultUserLoginVOwechatLogin(UserLoginDTOuserLoginDTO){StringopenidgetOpenid(userLoginDTO);// 判断openid是否为空if(openidnull){thrownewLoginFailedException(MessageConstant.LOGIN_FAILED);}// 判断是否是新用户UseruserlambdaQuery().eq(User::getOpenid,openid).one();// 如果是新用户自动完成注册if(usernull){userUser.builder().openid(openid).build();save(user);}LonguserIduser.getId();// 创建jwt令牌MapString,ObjectmapnewHashMap();map.put(JwtClaimsConstant.USER_ID,userId);StringtokenJwtUtil.createJWT(jwtProperties.getUserSecretKey(),jwtProperties.getUserTtl(),map);// 封装vo对象UserLoginVOuserLoginVOUserLoginVO.builder().id(userId).openid(userLoginDTO.getCode()).token(token).build();returnResult.success(userLoginVO);}privateStringgetOpenid(UserLoginDTOuserLoginDTO){// 调用微信接口实现登录MapString,StringLogin_MapnewHashMap();Login_Map.put(appid,weChatProperties.getAppid());Login_Map.put(secret,weChatProperties.getSecret());Login_Map.put(js_code,userLoginDTO.getCode());Login_Map.put(grant_type,authorization_code);StringresultHttpClientUtil.doGet(WECHAT_LOGIN_URL,Login_Map);log.info(微信接口返回数据{},result);// 解析JSONObjectjsonObjectJSON.parseObject(result);returnjsonObject.getString(openid);}}二、 StringRedisTemplate 与 RedisTemplate为了直观看到区别我们对比一下如果不加配置直接使用它们在 Redis 图形化工具如 RDM里长什么样。1. RedisTemplate(默认情况)Java 对象会被 JDK 序列化变成二进制流。AutowiredprivateRedisTemplateredisTemplate;publicvoidtestRedisTemplate(){// 存一个简单的字符串redisTemplate.opsForValue().set(city,beijing);}Redis 中实际存储的样子 (乱码):Key:\xac\xed\x00\x05t\x00\x04cityValue:\xac\xed\x00\x05t\x00\x07beijing缺点: 此时如果你去 Redis 命令行执行get city是查不到数据的因为 Key 实际上包含了乱码前缀。2. StringRedisTemplate (推荐)Key 和 Value 都是纯字符串所见即所得。AutowiredprivateStringRedisTemplatestringRedisTemplate;publicvoidtestStringRedisTemplate(){// 存一个简单的字符串stringRedisTemplate.opsForValue().set(city,beijing);// 存一个对象 (通常配合 JSON 工具库)UserusernewUser(1L,Jack);StringjsonUserJSON.toJSONString(user);// 手动转 JSONstringRedisTemplate.opsForValue().set(user:1,jsonUser);}Redis 中实际存储的样子 (清晰):Key:cityValue:beijingKey:user:1Value:{id:1, name:Jack}优点: 方便调试多语言通用。三、 Spring Cache 注解实战Spring Cache 的目标是把缓存逻辑从业务代码中剥离出来。在苍穹外卖中主要用于C端用户端查询套餐时的缓存提高响应速度。1. 开启缓存功能在启动类上必须添加注解SpringBootApplicationEnableCaching// --- 开启注解缓存功能publicclassSkyApplication{publicstaticvoidmain(String[]args){SpringApplication.run(SkyApplication.class,args);}}###2. 查询时自动缓存 (Cacheable)场景: 用户查询某个分类下的套餐列表。ServicepublicclassSetmealServiceImplimplementsSetmealService{// cacheNames (或 value): 缓存的名称相当于 Redis 中的文件夹// key: 具体的键名。这里使用 SpEL 表达式动态获取参数 categoryId// 最终 Redis Key 格式: setmealCache::1001 (假设 categoryId 为 1001)Cacheable(cacheNamessetmealCache,key#categoryId)publicListSetmeallist(LongcategoryId){// --- 下面的代码只有在缓存未命中时才会执行 ---SetmealsetmealnewSetmeal();setmeal.setCategoryId(categoryId);setmeal.setStatus(StatusConstant.ENABLE);ListSetmeallistsetmealMapper.list(setmeal);returnlist;// --- 方法结束后Spring 会自动将 list 返回值写入 Redis ---}}3. 修改时清理缓存 (CacheEvict)场景: 管理员在后台修改了套餐或者新增了套餐必须删除缓存防止用户看到旧数据。精确清理 (删除特定 key):// 当执行批量删除套餐时我们很难知道具体的 categoryId 是多少或者影响太大// 所以通常不精确删除而是直接删除整个 setmealCache 下的所有数据全部清理 (常用):ServicepublicclassSetmealServiceImplimplementsSetmealService{// allEntries true: 表示删除 setmealCache 下所有的 key// 无论是 setmealCache::1001 还是 setmealCache::1002 统统删掉CacheEvict(cacheNamessetmealCache,allEntriestrue)publicvoidsave(SetmealDTOsetmealDTO){// 执行正常的数据库新增操作SetmealsetmealnewSetmeal();BeanUtils.copyProperties(setmealDTO,setmeal);setmealMapper.insert(setmeal);// --- 方法执行完后Spring 自动清空 setmealCache 下的所有缓存 ---}CacheEvict(cacheNamessetmealCache,allEntriestrue)publicvoidupdate(SetmealDTOsetmealDTO){// update 逻辑...}}总结 Spring Cache 流程图查询请求- 拦截器 -Redis 有吗有- 直接返回数据 (Service 方法体代码不执行)。无- 执行 Service 方法 - 查数据库 - 返回结果 -自动存入 Redis。修改请求- 执行 Service 方法 - 改数据库 -自动删除 Redis 缓存。