跳至主要內容

项目构建

holic-x...大约 7 分钟项目itc

项目构建

安全框架引入

引入Shiro框架,构建用户安全认证

考虑到原有的实现方式是通过request来进行校验,每次都要请求获取用户参数信息。不能够灵活地在任一处获取到当前的登陆用户信息,例如controller=》service,要把request对象传过去,这种方式非常麻烦。

有一种方式是通过缓存方式存储:借助ThreadLoal存储登陆用户信息,

还有一种方式借助安全认证框架统一管理,引入shiro

原有登陆实现session存储登陆用户信息,自定义@AuthCheck注解和AOP 校验AuthInterceptor(权限拦截器)

取消原有session机制管理用户,交由Shiro框架统一管理用户登陆信息

后端接口调整:

  • 用户登陆:userController/userLogin=》accountController/login
  • 用户注销:userController/userLogout=》accountController/logout
  • 获取登陆用户信息:userController/getLoginUser=》accountController/getCurrentLoginUser
  • 接口排查:使用shiro接管了用户登陆状态管理,则项目中原有的一些内容已经不再适用,类似获取登陆用户信息、request(从session中获取内容等)都失效需要进行重构,此外鉴权(@AuthCheck也由shiro进行接管

PS:😤😤😤给后续代码排查留个坑

前端调整:

  • 对应前端登陆引用调整:注意fetchUserInfo、登陆方法等相关进行替换,校验登陆流程(项目中用到登陆、注销、获取用户登陆信息相关的接口都要进行排查,否则报错),如果出现登陆之后用户信息没更新则需要排查接口交互响应参数

​ 构建参考思路:之前写的笔记代码复盘:springboot整合shiro单用户、多用户模式构建

基础参考:https://blog.csdn.net/heyl163_/article/details/131504939

参考修改代码说明:

  • pom.xml:引入shiro框架
  • ShiroConfig:全局配置
  • SingleShiroUtil:单类型用户Shiro工具类
  • UserRealm:自定义鉴权机制(用户登陆校验、用户访问鉴权)
  • AccountController:登陆、注销、获取当前登录用户信息接口定义,提供shiro提供的注解实现鉴权
  • AccountService、AccountServiceImpl:相关实现方法定义,其业务逻辑调整为基于Shiro进行构建
  • application.yml:自定义controller层需要配置swagger,让它可以扫描到新引入的内容
  • 其他代码原有逻辑不变,但是此前所有基于session相关的操作需要取消(例如获取登陆用户信息、鉴权等凡是涉及和登陆用户相关的都由shiro接管)

​ 解决业务代码request无限传递的问题,可以通过自定义工具类在任意位置拿到当前登陆用户信息,而不用依托于request的session

 /**
         * RequestContextHolder 获取请求信息:此处涉及到一个问题,因为request是游离这个接口规范以外的所需参数,因此在处理的时候要考虑两个方面的问题
         * 1.不满足规范考虑剔除,对接参数需求额外提供接口进行处理
         * 2.尽量自主获取到参数信息,此处借助RequestContextHolder获取请求信息从而拿到所需数据,但也会引申一个问题:当请求来源不同的时候这个request可能和系统所需的有所出入
         * 3.修改规范:确认其他接口是否也是需要这个参数,但这个改造成本可能在后期会显得大,因为一些现有的接口已经按照既定规范执行,唯恐牵一发动全身
         */
//        ServletRequestAttributes servletRequestAttributes =  (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
//        HttpServletRequest request = servletRequestAttributes.getRequest();

用户模块完善

个人中心

​ 首先梳理个人中心功能构建的思路:

​ 修改AvatarDropdown.tsx文件的menuItems内容,参考退出登录功能实现(填充个人中心信息),先定义菜单组件,然后跟踪onMenuClick按钮点击事件触发,可以看到当key为退出的时候触发调用登录退出接口,其他的key都会通过history.push进行页面转发(此处则可根据其具体转发的地址配置相关的路由并实现页面即可)

image-20240428105214419

​ 修改routes:把个人中心页面加载进去(layout设置为false,则会跳转到一个空页面(没有任何样式),如果设置为false则会嵌入导航栏)

// 账号信息相关
{
    path: '/account',
    // layout: false,  设置为false不引用任何样式,如果为true会自动嵌入导航栏,如果想自定义页面样式可以通过配置这个参数进行控制
    routes: [
      {name: '个人中心', path: '/account/center', component: './Account/Center'},
      {name: '个人设置', path: '/account/settings', component: './Account/Settings'},
    ],
  },

​ 参考index.tsx实现(实现效果为跳转到指定页面,这个页面规则由div进行控制:不会单独嵌入一些路由)


import { useModel } from '@umijs/max';
import React, { useEffect,useState } from 'react';
import {getLoginUserUsingGet} from "@/services/itc-platform/userController";


const Index: React.FC = () => {
  const [type, setType] = useState<string>('account');
  const { setInitialState } = useModel('@@initialState');

  useEffect(()=>{
    getLoginUserUsingGet({}).then(res=>{
      console.error('res',res)
    })
  });

  return (

    // 页面信息定义(自定义样式)
    <div className = "my-index">
      hello my index
    </div>

  );
};
export default Index;

​ 借助PageContainer封装(实现效果为设定导航栏控制,会根据PageContainer嵌入一些内容)


import React from 'react';
import { PageContainer } from '@ant-design/pro-components';

const Index: React.FC = () => {
  return (
    <PageContainer>
      hello my index ssss
    </PageContainer>
  );
};
export default Index;

image-20240428112444211

​ 参考图标信息:Ant Design 图标open in new window图标定义参考open in new window

前后台:页面定义

针对管理页面:可以共用组件,但是需要结合用户角色设定不同的访问权限(路由配置导向不同路径,指向同一个组件)

控制数据访问权限和操作权限即可

针对接口信息(需限定权限,管理员不可以违规操作内容)

签到功能

数据表设计

-- 用户签到表
create table user_sign
(
    id         bigint  							comment 'id' primary key,
    uid        bigint                      		  not null comment '签到用户ID',
    title      varchar(512)                       not null comment '签到说明',
    signInTime datetime                           not null comment '开始时间',
    signChannel      varchar(512)                 comment '签到渠道',
    score      int(11)  default 0                 comment '获取积分',
    isDelete   tinyint  default 0                 not null comment '是否删除'
);

开发小技巧

初始化管理页面模板参考

最基础的管理页面(PageContainer)


调用接口,自定义div


import { useModel } from '@umijs/max';
import React, { useEffect,useState } from 'react';
import {getLoginUserUsingGet} from "@/services/itc-platform/userController";


const Index: React.FC = () => {
  const [type, setType] = useState<string>('account');
  const { setInitialState } = useModel('@@initialState');

  useEffect(()=>{
    getLoginUserUsingGet({}).then(res=>{
      console.error('res',res)
    })
  });

  return (

    // 页面信息定义(自定义样式)
    <div className = "my-index">
      hello my index
    </div>

  );
};
export default Index;

获取登录用户信息

import {useModel} from "@@/exports";

  // 从全局状态中获取登录用户信息
  const { initialState } = useModel('@@initialState');
  const { currentUser } = initialState || {};

React在页面渲染之前调用接口获取数据,随后渲染页面

构建思路

1)使用useState定义变量保存、设定数据

2)使用userEffect钩子在组件渲染之前调用接口获取数据(useEffect在组件挂载后和每次依赖更新后执行,可以在useEffect中放置你的数据获取逻辑)

3)使用loading阻塞(如果数据还没有完全返回,组件渲染需要用到数据中的某些内容,则可能会报错提示,因此可以自主设定loading阻塞,如果数据还没返回就先不渲染组件)

参考构建步骤

import React, { useState, useEffect } from 'react';
import axios from 'axios';
 
function MyComponent() {
  const [data, setData] = useState(null);
 
  useEffect(() => {
    const fetchData = async () => {
      const result = await axios(
        '你的API接口URL',
      );
      setData(result.data);
    };
    fetchData();
  }, []); // 空数组意味着仅在组件挂载时调用一次
 
  if (!data) {
    return <div>Loading...</div>;
  }
 
  return (
    <div>
      {/* 渲染获取到的数据 */}
      <h1>Data: {data.attribute}</h1>
    </div>
  );
}
 
export default MyComponent;

参考实现说明(例如想要初始化页面加载数据然后封装组件)

import React ,{ useEffect,useState }from 'react';

// 接口服务引入
import {getUserVoMore} from "@/services/itc-platform/userController";

const Index: React.FC = () => {

  // 1.定义全局变量,存储接口调用的响应数据(此处为用户详细信息)
  const [userMoreInfo,setUserMoreInfo] = useState();

  // 2.调用接口获取数据详情信息(空数组表示仅在组件挂载时调用一次)
  useEffect(()=>{
    getUserVoMore().then(res=>{
      // 根据请求接口响应设定该值
      setUserMoreInfo(res.data);
      console.error('center响应数据',res)
    });
  },[])

  // 3.如果请求还没加载完成,则等待(否则待组件加载完成数据还没请求完,就会提示渲染报错)
  if (!userMoreInfo) {
    return <div>Loading...</div>;
  }

  // 4.其他处理操作定义
  - ....... -

  // 组件渲染
  return (
    <PageContainer>
      /* 处理数据并展示 */
    </PageContainer>
  );
};
export default Index;

PS:扩展:可自定义loading加载样式

  const loading = (
    <span className={styles.action}>
      <Spin
        size="small"
        style={{
          marginLeft: 8,
          marginRight: 8,
        }}
      />
    </span>
  );

  // 如果请求还没加载完成,则等待(否则待组件加载完成数据还没请求完,就会提示渲染报错)
  if (!userMoreInfo) {
    // return <div>Loading...</div>;
    return loading;

  }
评论
  • 按正序
  • 按倒序
  • 按热度
Powered by Waline v3.1.3