做个网站设计多少钱,中国学校网站前台模板,给个网站最新的2021,制作网站需要多少时间目录 前言一、创建工程项目#x1f38d;1.1 创建后端工程1.2 创建前端工程 二、业务代码#x1f38a;后端代码前端代码 三、测试参考资料 前言
Sa-Token 是一款 Java 语言的权限认证框架#xff0c;提供了灵活、高效、易用的权限认证和会话管理功能。它是 SpringBoot、Spri… 目录 前言一、创建工程项目1.1 创建后端工程1.2 创建前端工程 二、业务代码后端代码前端代码 三、测试参考资料 前言
Sa-Token 是一款 Java 语言的权限认证框架提供了灵活、高效、易用的权限认证和会话管理功能。它是 SpringBoot、Spring MVC、Servlet 等 Java 技术体系下的轻量级权限认证组件可以帮助开发者快速实现用户认证、授权和会话管理等功能。 Sa-Token官方文档
功能结构图 认证流程图 框架特性 一、创建工程项目
1.1 创建后端工程 引入依赖 parentgroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-parent/artifactIdversion2.6.13/versionrelativePath/ !-- lookup parent from repository --
/parentdependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactId
/dependency
!-- Sa-Token 权限认证在线文档https://sa-token.cc --
dependencygroupIdcn.dev33/groupIdartifactIdsa-token-spring-boot-starter/artifactIdversion1.38.0/version
/dependencydependencygroupIdcn.dev33/groupIdartifactIdsa-token-redis-fastjson2/artifactIdversion1.37.0/version
/dependency
!--无论使用哪种序列化方式你都必须为项目提供一个 Redis 实例化方案--
dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-data-redis/artifactId
/dependencydependencygroupIdorg.projectlombok/groupIdartifactIdlombok/artifactIdoptionaltrue/optional
/dependency注如果你使用的是 SpringBoot 3.x只需要将 sa-token-spring-boot-starter 修改为 sa-token-spring-boot3-starter 即可。 基础配置 server:port: 8081
spring:redis:database: 1host: 127.0.0.1port: 6379password:timeout: 5000jedis:pool:max-active: 8max-wait: -1max-idle: 8min-idle: 0
#Sa-token相关配置与官网一致
sa-token: # token 名称同时也是 cookie 名称token-name: satoken# token 有效期单位秒 默认30天-1 代表永久有效timeout: 2592000# token 最低活跃频率单位秒如果 token 超过此时间没有访问系统就会被冻结默认-1 代表不限制永不冻结active-timeout: -1# 是否允许同一账号多地同时登录 为 true 时允许一起登录, 为 false 时新登录挤掉旧登录is-concurrent: true# 在多人登录同一账号时是否共用一个 token 为 true 时所有登录共用一个 token, 为 false 时每次登录新建一个 tokenis-share: true# token 风格默认可取值uuid、simple-uuid、random-32、random-64、random-128、tiktoken-style: uuid# 是否输出操作日志 is-log: true1.2 创建前端工程
本项目使用的vue3创建一个vue项目引入下方依赖即可
dependencies: {axios: ^1.6.8,element-plus: ^2.7.3,pinia: ^2.1.7,qs: ^6.12.1,vue: ^3.2.37,vue-router: ^4.2.5
},二、业务代码
后端代码
User.java
Data
public class User{//idTableId(type IdType.ASSIGN_ID)private Long id;//用户名private String username;//密码private String password;//账户是否锁住(1被锁0未被锁)private Integer isLocked;//账户是否被删除(1删除0未被删除)TableLogicprivate Integer isDelete;//创建时间TableField(fill FieldFill.INSERT)private Date createTime;//更新时间TableField(fill FieldFill.INSERT_UPDATE)private Date updateTime;
}UserDTO.java
EqualsAndHashCode(callSuper true)
Data
public class UserDTO extends User {//记住我private boolean rememberMe;//角色列表private ListRole roleList;//登录的设备private String device;//图形验证码private String code;//图形验证码的keyprivate String codeKey;}UserController.java
注意代码中定义了一些常量可以自行替代。
RestController
RequestMapping(/user)
CrossOrigin
public class UserController {AutowiredUserService userService;AutowiredStringRedisTemplate stringRedisTemplate;PostMapping(/doLogin)public SaResult doLogin(RequestBody UserDto userDto) {//从redis中取生成的验证码String validateCode stringRedisTemplate.opsForValue().get(code:validate: userDto.getCodeKey());if (userDto.getCode().equals(validateCode)) {//验证码正确才进行用户验证先验证了用户存不存在再验证了密码是否正确if (userService.login(userDto.getUsername(), userDto.getPassword())) {StpUtil.login(userDto.getUsername(), new SaLoginModel()//实现‘记住我’功能.setIsLastingCookie(userDto.isRememberMe())//设置登录设备主要用于实现同端互斥登录此处没有实现该功能可以不用管.setDevice(PC));//验证成功就以json的形式返回tokenHashMapString, Object resultMap new HashMap();resultMap.put(token, StpUtil.getTokenValue());return SaResult.ok(ResponseCode.LOGIN_SUCCESS.getMessage()).setCode(ResponseCode.LOGIN_SUCCESS.getCode()).setData(resultMap);} else {return SaResult.error(ResponseCode.USERNAME_PASSWORD_ERROR.getMessage()).setCode(ResponseCode.USERNAME_PASSWORD_ERROR.getCode());}} else {return SaResult.error(ResponseCode.VALIDATE_CODE_ERROR.getMessage()).setCode(ResponseCode.VALIDATE_CODE_ERROR.getCode());}}}
StpUtils.login
检查此账号是否之前已有登录为账号生成 Token 凭证与 Session 会话记录 Token 活跃时间通知全局侦听器xx 账号登录成功将 Token 注入到请求上下文等等其它工作……
SaResult这个也是一个由Satoken封装的结果响应类还是挺好用的。
UserService.java
public interface UserService {boolean login(String username, String password);
}UserServiceImpl.java
Service
public class UserServiceImpl implements UserService{AutowiredUserDao userDao;Overridepublic boolean login(String username, String password) {User user userDao.selectOne(new QueryWrapperUser().eq(username, username));//数据库中的密码进行了加密BCrypt也是Satoken提供的一个工具类return user ! null BCrypt.checkpw(password, user.getPassword());}
}UserDao.java
/*** (User)表数据库访问层** author yzk* since 2024-05-15 16:44:29*/
public interface UserDao extends BaseMapperUser {}SaAuthenticationConfigure.java
Configuration
public class SaAuthenticationConfigure implements WebMvcConfigurer {// 注册 Sa-Token 拦截器打开注解式鉴权功能 Overridepublic void addInterceptors(InterceptorRegistry registry) {// 注册 Sa-Token 拦截器打开注解式鉴权功能registry.addInterceptor(new SaInterceptor(handle - StpUtil.checkLogin()))//所有接口都会检查是否登录了.addPathPatterns(/**)//以下接口不检查直接放行.excludePathPatterns(/user/doLogin)}}SaTokenFilter.java
Configuration
public class SaTokenFilter implements WebMvcConfigurer {/*** 注册 [Sa-Token 全局过滤器] */Beanpublic SaServletFilter getSaServletFilter() {return new SaServletFilter()// 指定 [拦截路由] 与 [放行路由].addInclude(/**).addExclude(/favicon.ico)// 认证函数: 每次请求执行 .setAuth(obj - {SaManager.getLog().debug(----- 请求path{} 提交token{}, SaHolder.getRequest().getRequestPath(), StpUtil.getTokenValue());// ...})// 异常处理函数每次认证函数发生异常时执行此函数 .setError(e - {return SaResult.error(e.getMessage());})// 前置函数在每次认证函数之前执行.setBeforeAuth(obj - {SaHolder.getResponse()// ---------- 设置跨域响应头 ----------// 允许指定域访问跨域资源.setHeader(Access-Control-Allow-Origin, *)// 允许所有请求方式.setHeader(Access-Control-Allow-Methods, *)// 允许的header参数.setHeader(Access-Control-Allow-Headers, *)// 有效时间.setHeader(Access-Control-Max-Age, 3600);// 如果是预检请求则立即返回到前端 SaRouter.match(SaHttpMethod.OPTIONS).free(r - System.out.println(--------OPTIONS预检请求不做处理)).back();});}}注意这个过滤器务必配置这个过滤器用SaToken时后端使用CrossOrigin进行了跨域的配置但是前端发起请求还是会报跨域问题而且后端会报未读取到有效token是因为前端发起请求时先发起了预检请求SaToken拦截了预检请求预检请求的headers中没有token所以一直报错。
前端代码
此处删减了部分代码只保留了登录登录相关的代码
login.vue
templatediv classlogin-containerh2登录/h2form submit.preventhandleSubmitdiv classform-grouplabel forusername用户名:/labelel-input typetext idusername v-modeluser.username placeholder请输入用户名 required//divdiv classform-grouplabel forpassword密码:/labelel-input typepassword idpassword v-modeluser.password placeholder请输入密码 required//divdiv classform-grouplabel验证码/labelel-row :gutter15el-col :span14el-input v-modeluser.code stylemargin-top: 7px classcodeInputplaceholder请输入验证码/el-input/el-colel-col :span10el-image clickchangeImage stylewidth: 159px; height: 39px :srcimageUrl//el-col/el-row/divdiv classform-groupel-checkbox v-modeluser.rememberMe label记住我 sizelarge//divbutton typesubmit登录/buttonbutton typesubmit clicktoRegister注册/button/formdiv classsocial-loginbutton clicktoGiteespan classsocial-icon/span使用Gitee登录/button/div/div
/templatescript setup
import {h, onMounted, ref} from vue;
import {generatorLoginCode, toGiteeOauth, userLogin} from /api/userApi;
import {useRouter} from vue-router;
import {ElCheckbox, ElCol, ElImage, ElInput, ElNotification, ElRow} from element-plus;const router useRouter()
const user ref({username: ,password: ,rememberMe: false,code: ,codeKey:
})
const handleSubmit () {userLogin(JSON.stringify(user.value)).then((resp) {if (resp.code 2000) {//取出token并存入localStoragelocalStorage.setItem(token, resp.data.token)ElNotification({title: 提示,message: h(info, {style: color: teal}, resp.msg),duration: 3000})router.push(/main)} else {if (resp.code 5005) {ElNotification({title: 提示,message: h(error, {style: color: red}, resp.msg),duration: 3000})changeImage()}}})
};
const userLogin (data) {return requests({url: /user/doLogin,data: data,method: POST,})
}
/scriptstyle scoped/style前端点击登录按钮时发后端接口发起请求请求成功后取出响应结果中的token存入localStorage。
requests.js
import axios from axios;
import {router} from /router;axios.defaults.crossDomain trueexport const requests axios.create({baseURL: http://localhost:8081,timeout: 10000, // 请求超时时间headers: {Content-Type: application/json,Access-Control-Allow-Origin: *}
});// 请求拦截器
requests.interceptors.request.use((config) {// 在发送请求之前做些什么const token localStorage.getItem(token); // 假设token存储在localStorage中if (token) {config.headers.Token Bearer ${token}; // 添加token到请求头}return config;},(error) {// 对请求错误做些什么return Promise.reject(error);}
);// 响应拦截器
requests.interceptors.response.use((response) {console.log(response.data.code)const coderesponse.data.codeif (code 5006 ||code5007|| code5008) {//返回登录界面router.push(/).then(r {location.reload()} )// router.go(0)//删除当前localStorage中的tokenlocalStorage.removeItem(token)}return response.data;},(error) {// 对响应错误做点什么return Promise.reject(error);}
);注意请求拦截器每次发起请求时都会从localStorage中取出token并将其放入headers中
三、测试
集成了redis后记得先开启redis服务在登录时框架会自动保存数据 开启前后端服务 本文的代码是从完整项目中抽出的上述代码只有登录功能
输入用户名和密码后进入主界面 此时登录的信息已经自动的被框架自动保存进redis IDEA控制台打印出相应的信息 参考资料
Sa-Token
使用 Sa-Token 的全局过滤器解决跨域问题三种方式全版 - 掘金 (juejin.cn)
Sa-Token功能结构图