Springboot項目,SpringBoot日記本系統全程直播03:把登錄后臺接起來撒~~

 2023-10-15 阅读 30 评论 0

摘要:上一節:SpringBoot日記本系統全程直播02:登錄頁面搞起來撒~~ 大家好,我是今天晚上的主講老師,我是兔哥。 Springboot項目。上一講,我們學習了登錄和注冊頁面,以及Controller訪問頁面的方法,還有如何處理靜態資源。 今天我們

上一節:SpringBoot日記本系統全程直播02:登錄頁面搞起來撒~~

大家好,我是今天晚上的主講老師,我是兔哥。

Springboot項目。上一講,我們學習了登錄和注冊頁面,以及Controller訪問頁面的方法,還有如何處理靜態資源。

今天我們繼續來學習SpringBoot日記本系統,任務是進行后臺的對接。就是說,用戶注冊和登錄的功能,需要完善起來啦。還會涉及到很多企業級開發技術哦。

總之,這一節的內容非常之多,也比較豐富。視頻我后期會補上,如果跟著做做不出來,一定要下載源碼慢慢比對哈。

ok,那么我們現在開整!

目錄

1.數據庫建表

1.1 為什么不用UUID?

1.2 假如以后分庫分表,ID重復怎么辦?

2. 登錄/注冊頁面訪問后臺

3.后端準備工作

3.1 系統核心包

3.1.1?BizException 業務異常類

3.1.2?GlobalExceptionHandler 全局異常處理

3.1.3?ExceptionCodeEnum?通用異常枚舉

3.1.4?CommonResponseDataAdvice 統一封賬響應結果

3.1.5?IgnoreCosmoResult 忽略統一結果返回封裝

3.2 用戶類和mybatisplus引入

3.3 redis引入(為了ID自增)

4. sa-token 鑒權框架

5. 用戶接口和服務類

6. 如何下載源碼?


1.數據庫建表

navicat大家應該都有吧,或者類似的軟件也可以,創建一個數據庫,名字叫diary。

然后創建用戶表:

CREATE TABLE `user_base` (
`uid`  bigint(20) NOT NULL COMMENT '用戶ID' ,
`user_role`  tinyint(2) UNSIGNED NOT NULL DEFAULT 1 ,
`register_source`  tinyint(4) UNSIGNED NOT NULL DEFAULT 0 COMMENT '注冊來源:1手機號 2郵箱 3用戶名 4qq 5微信 6騰訊微博 7新浪微博' ,
`user_name`  varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '用戶賬號,必須唯一' ,
`password`  varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '密碼' ,
`nick_name`  varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '用戶昵稱' ,
`gender`  tinyint(1) UNSIGNED NOT NULL DEFAULT 0 COMMENT '用戶性別 0-female 1-male' ,
`birthday`  bigint(20) UNSIGNED NOT NULL DEFAULT 0 COMMENT '用戶生日' ,
`signature`  varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '用戶個人簽名' ,
`mobile`  varchar(16) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '手機號碼(唯一)' ,
`mobile_bind_time`  varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '0' COMMENT '手機號碼綁定時間' ,
`email`  varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '郵箱(唯一)' ,
`email_bind_time`  varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '0' COMMENT '郵箱綁定時間' ,
`face`  varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '頭像' ,
`face200`  varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '頭像 200x200x80' ,
`srcface`  varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '' COMMENT '原圖頭像' ,
`create_time`  varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '創建時間' ,
`update_time`  varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '修改時間' ,
PRIMARY KEY (`uid`)
)
ENGINE=InnoDB
DEFAULT CHARACTER SET=utf8mb4 COLLATE=utf8mb4_general_ci
COMMENT='用戶基礎信息表'
ROW_FORMAT=COMPACT
;

把上面的sql拖到navicat中執行,建表成功。

?

關于UID,我們用bigint類型。

1.1 為什么不用UUID?

因為UUID是看不出順序的,我們用int類型可以看出用戶創建的先后順序。

1.2 假如以后分庫分表,ID重復怎么辦?

我們知道mysql沒有sequence,字段不是自增的。如果用自增長ID,分庫分表的話,很難保證ID不重復和連續。因此,我們可以采用redis的ID自增長策略,這樣不管你有幾個數據庫,只要redis是一個,就沒問題啦。(本節后面會給出具體的方案)

2. 登錄/注冊頁面訪問后臺

回顧下注冊頁面

這一步,我們需要對接后臺的登錄注冊接口。

引入vue,axios

<script src="${basePath}/js/vue.js"></script>
<script src="${basePath}/js/axios.min.js"></script>

?更改HTML

<div class="container" id="app"><div class="register-box"><h2 class="register-title"><span>沒有賬號,去</span>注冊日記本</h2><div class="input-box"><input type="text" v-model="userName1" placeholder="用戶名" autocomplete="off"><input type="password" v-model="password1" placeholder="密碼" autocomplete="off"><input type="password" v-model="password11" placeholder="確認密碼" autocomplete="off"></div><button @click="reg">注冊</button></div><div class="login-box slide-up"><div class="center"><h2 class="login-title"><span>已有賬號,去</span>登錄日記本</h2><div class="input-box"><input type="text" v-model="userName2" placeholder="用戶名" autocomplete="off"><input type="password" v-model="password2" placeholder="密碼" autocomplete="off"></div><button @click="login">登錄</button></div></div>
</div>

其實用jquery是完全可以的,不過現在jq基本沒人用了,于是本項目我們也趕一下潮流吧。

vue代碼:

let vue = new Vue({el:'#app',data: {//注冊userName1:'',password1:'',password11:'',//登錄userName2:'',password2:''},methods:{reg(){if(this.password1 != this.password11){layer.msg('兩次輸入密碼不一致',{icon:2});return;}let data = {userName: this.userName1,password: this.password1}axios.post('${basePath}/user/register',data).then(r =>{if(r.data.code != '0000'){layer.msg(r.data.message,{icon:2});return;}layer.msg('注冊成功!',{icon:2});});},login(){let data = {userName: this.userName2,password: this.password2}axios.post('${basePath}/user/login',data).then(r =>{if(r.data.code != '0000'){layer.msg(r.data.message,{icon:2});return;}layer.msg('登錄成功!',{icon:1});//登錄成功后,就緩存賬號密碼localStorage.setItem('userName2',this.userName2);localStorage.setItem('password2',this.password2);setTimeout(()=>{location.href="/";},1000)});},//填充緩存的賬號和密碼fillAccount(){let userName2 = localStorage.getItem('userName2');let password2 = localStorage.getItem('password2');if(userName2){this.userName2 = userName2;}if(password2){this.password2 = password2;}if(userName2 && password2){setTimeout(()=>{document.querySelector('.login-title').click();},1000)}}},created(){this.fillAccount();}
});

登錄流程:獲取用戶名和密碼,發送 /user/login?

返回報文格式是這樣的:

?這個格式后臺是怎么返回的,下面會說到。登錄成功后,會把用戶名密碼緩存到本地,緩存技術用的是localStorage。代碼如下:

login(){let data = {userName: this.userName2,password: this.password2}axios.post('${basePath}/user/login',data).then(r =>{if(r.data.code != '0000'){layer.msg(r.data.message,{icon:2});return;}layer.msg('登錄成功!',{icon:1});//登錄成功后,就緩存賬號密碼localStorage.setItem('userName2',this.userName2);localStorage.setItem('password2',this.password2);setTimeout(()=>{location.href="/";},1000)});
},

?注冊流程:校驗兩次密碼輸入是否一致,然后發送用戶名和密碼到?/user/register?

代碼如下:

reg(){if(this.password1 != this.password11){layer.msg('兩次輸入密碼不一致',{icon:2});return;}let data = {userName: this.userName1,password: this.password1}axios.post('${basePath}/user/register',data).then(r =>{if(r.data.code != '0000'){layer.msg(r.data.message,{icon:2});return;}layer.msg('注冊成功!',{icon:2});});},

如果已經發現緩存中存在用戶名和密碼,就自動填充,并且顯示登陸頁面:

fillAccount(){let userName2 = localStorage.getItem('userName2');let password2 = localStorage.getItem('password2');if(userName2){this.userName2 = userName2;}if(password2){this.password2 = password2;}if(userName2 && password2){setTimeout(()=>{document.querySelector('.login-title').click();},1000)}
}

如果你已經登錄過,那就會看到一個向上滑動的效果,然后系統會自動填充用戶名和密碼。

OK,以上就是前端的邏輯了。

3.后端準備工作

接下來,我們需要做一點準備工作。代碼有點多,我們一個個來看。

3.1 系統核心包

創建上圖所示核心包,位置:

com.rabbit.diary.bean.core

3.1.1?BizException 業務異常類

?這是我們系統自定義的業務異常類,所有業務上的報錯,比如【用戶名密碼錯誤】之類的,我們就需要拋出這個異常。

package com.rabbit.diary.bean.core;/*** 業務異常* biz是business的縮寫** @author sunting* @see ExceptionCodeEnum*/
public class BizException extends RuntimeException {private ExceptionCodeEnum error;/*** 構造器,有時我們需要將第三方異常轉為自定義異常拋出,但又不想丟失原來的異常信息,此時可以傳入cause** @param error* @param cause*/public BizException(ExceptionCodeEnum error, Throwable cause) {super(cause);this.error = error;}/*** 構造器,只傳入錯誤枚舉** @param error*/public BizException(ExceptionCodeEnum error) {this.error = error;}public ExceptionCodeEnum getError() {return error;}public void setError(ExceptionCodeEnum error) {this.error = error;}}

3.1.2?GlobalExceptionHandler 全局異常處理

作用是捕獲異常,當發生異常的時候,就會進入對應的handler。

package com.rabbit.diary.bean.core;import cn.dev33.satoken.exception.NotLoginException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;/*** 全局異常處理* 一般來說,全局異常處理只是一種兜底的異常處理策略,也就是說提倡自己處理異常。* 但現在其實很多人都喜歡直接在代碼中拋異常,全部交給@RestControllerAdvice處理*/
@RestControllerAdvice
public class GlobalExceptionHandler {/*** 權限異常 (注意,參數一定要是需要捕獲的異常,否則進不來)* @param* @return*/@ExceptionHandler(NotLoginException.class)public com.rabbit.diary.bean.core.Result<com.rabbit.diary.bean.core.ExceptionCodeEnum> handleNotLoginException(NotLoginException bizException) {return com.rabbit.diary.bean.core.Result.error(com.rabbit.diary.bean.core.ExceptionCodeEnum.NEED_LOGIN,bizException.getMessage());}/*** 業務異常(需要主動拋出)** @param* @return*/@ExceptionHandler(com.rabbit.diary.bean.core.BizException.class)public com.rabbit.diary.bean.core.Result<com.rabbit.diary.bean.core.ExceptionCodeEnum> handleBizException(com.rabbit.diary.bean.core.BizException bizException) {return com.rabbit.diary.bean.core.Result.error(bizException.getError());}/*** 運行時異常** @param e* @return*/@ExceptionHandler(RuntimeException.class)public com.rabbit.diary.bean.core.Result<com.rabbit.diary.bean.core.ExceptionCodeEnum> handleRunTimeException(RuntimeException e) {return com.rabbit.diary.bean.core.Result.error(com.rabbit.diary.bean.core.ExceptionCodeEnum.ERROR);}}

3.1.3?ExceptionCodeEnum?通用異常枚舉

package com.rabbit.diary.bean.core;/*** 通用異常枚舉* @author Administrator**/
public enum ExceptionCodeEnum {SUCCESS("0000","返回成功!"),ERROR("1111","與服務方通訊失敗,請聯系管理員!"),NEED_LOGIN("9999", "用戶未登錄!"),ERROR_PARAM("1000", "參數送錯了!"),EMPTY_PARAM("2000", "參數為空!"),;private String code;private String desc;private ExceptionCodeEnum(String code, String desc) {this.code = code;this.desc = desc;}public String getCode() {return code;}public void setCode(String code) {this.code = code;}public String getDesc() {return desc;}//為了封裝自定義信息,做特殊處理public ExceptionCodeEnum setDesc(String desc) {this.desc = desc;return this;}}

3.1.4?CommonResponseDataAdvice 統一封賬響應結果

作用是設置通用的返回,以后在RestController里面寫方法,就不需要專門設置返回結果了。

/*** 統一封賬響應結果* @author Administrator**/
@RestControllerAdvice
public class CommonResponseDataAdvice implements ResponseBodyAdvice<Object> {@Overridepublic boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) {// 標注了@RestController,且類及方法上都沒有標注@IgnoreCosmoResult的方法才進行包裝return methodParameter.getDeclaringClass().isAnnotationPresent(RestController.class)&& !methodParameter.getDeclaringClass().isAnnotationPresent(com.rabbit.diary.bean.core.IgnoreCosmoResult.class)&& !methodParameter.getMethod().isAnnotationPresent(com.rabbit.diary.bean.core.IgnoreCosmoResult.class);}@Overridepublic Object beforeBodyWrite(Object o,MethodParameter methodParameter,MediaType mediaType,Class<? extends HttpMessageConverter<?>> aClass,ServerHttpRequest serverHttpRequest,ServerHttpResponse serverHttpResponse) {// 已經包裝過的,不再重復包裝if (o instanceof com.rabbit.diary.bean.core.Result) {return o;}// 改一行代碼即可:把Object返回值用Result封裝return com.rabbit.diary.bean.core.Result.success(o);}
}

3.1.5?IgnoreCosmoResult 忽略統一結果返回封裝

和上一個類配套使用,如果有的方法或者類不希望被統一封裝,就加上這個注解。

/*** 如果有的方法不希望被統一封裝結果,就用這個注解* @author Administrator**/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface IgnoreCosmoResult {}

3.1.6?Result 通用返回結果

public class Result<T> implements Serializable{private String code;private String message;private T data;private Result(String code, String message, T data) {this.code = code;this.message = message;this.data = data;}private Result(String code, String message) {this.code = code;this.message = message;this.data = null;}/*** 帶數據成功返回** @param data* @param <T>* @return*/public static <T> Result<T> success(T data) {return new Result<>(ExceptionCodeEnum.SUCCESS.getCode(), ExceptionCodeEnum.SUCCESS.getDesc(), data);}/*** 不帶數據成功返回** @return*/public static <T> Result<T> success() {return success(null);}/*** 通用錯誤返回** @param exceptionCodeEnum* @return*/public static <T> Result<T> error(ExceptionCodeEnum exceptionCodeEnum) {return new Result<>(exceptionCodeEnum.getCode(), exceptionCodeEnum.getDesc());}/*** 通用錯誤返回** @param exceptionCodeEnum* @param msg* @return*/public static <T> Result<T> error(ExceptionCodeEnum exceptionCodeEnum, String msg) {return new Result<>(exceptionCodeEnum.getCode(), msg);}/*** 通用錯誤返回** @param exceptionCodeEnum* @param data* @param <T>* @return*/public static <T> Result<T> error(ExceptionCodeEnum exceptionCodeEnum, T data) {return new Result<>(exceptionCodeEnum.getCode(), exceptionCodeEnum.getDesc(), data);}public String getCode() {return code;}public void setCode(String code) {this.code = code;}public String getMessage() {return message;}public void setMessage(String message) {this.message = message;}public T getData() {return data;}public void setData(T data) {this.data = data;}}

以上6個類的作用很大,幫我們解決了編寫Controller方法的數據返回問題,和異常拋出的問題。有了他們,可以極大地方便我們寫后面的代碼。

3.2 用戶類和mybatisplus引入

在第一小節,我們已經建好了表,接下來我們需要做一些Java后端的工作。首先是配置mybatis-plus:

pom.xml增加依賴

<!-- mybatis plus 支持 -->
<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.3.1.tmp</version>
</dependency>

application.yml配置myabtis

mybatis-plus:mapper-locations: classpath:mybatis/*.xmltype-aliases-package: com.rabbit.diary.bean

注意就是配置xml文件的存放位置,和JavaBean的路徑,JavaBean就是和數據庫表對應的類。

User.java編寫

@TableName("user_base")
@Data
@Service
public class User {@TableId(type = IdType.ASSIGN_UUID)private Long uid;private Integer userRole;private Integer registerSource;private String userName;private String password;private String nickName;private Integer gender;private String birthday;private String signature;private String mobile;private String mobileBindTime;private String email;private String emailBindTime;private String face;private String face200;private String srcface;private String createTime;private String updateTime;}

我用了lombok,這個需要安裝對應的idea插件,和maven依賴,當然你也可以選擇手動生成get,set方法。

lombok依賴:

<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.16</version>
</dependency>

因為使用了mybatis-plus,我們大部分情況, 其實是不需要再手動編寫xml了。

創建包:com.rabbit.diary.dao

編寫UserMapper.java

package com.rabbit.diary.dao;import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.rabbit.diary.bean.User;
import org.apache.ibatis.annotations.Mapper;@Mapper
public interface UserMapper extends BaseMapper<User> {
}

?這樣就有基本的增刪改查方法了。

3.3 redis引入(為了ID自增)

上面已經說過,為了應對分庫分表的情況(雖然這個項目用不到哈),我們將采用redis的方式。redis大家可以自行去百度下載windows版的,安裝很方便。

雙擊這個exe就可以打開了。

增加maven依賴:

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

springboot有對應的starter,引入了就行。

然后是application.yml配置

spring:redis:host: 127.0.0.1port: 6379password:jedis:pool:max-active: 8max-wait: -1max-idle: 500min-idle: 0lettuce:shutdown-timeout: 0timeout: 2s

注意是放在spring節點下,和mvc,datasource節點是同級的。如果還是不對,請下載源碼自行比較。一般而言,我們剛下載的redis是沒有密碼的。

redis輔助工具類:

IRedisService

public interface IRedisService {// 加入元素void setValue(String key, Map<String, Object> value);// 加入元素void setValue(String key, String value);// 加入元素void setValue(String key, Object value);// 獲取元素Object getMapValue(String key);// 獲取元素Object getValue(String key);
}
RedisServiceImpl
package com.rabbit.diary.redis;import com.rabbit.diary.util.SpringUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.data.redis.support.atomic.RedisAtomicLong;
import org.springframework.stereotype.Service;import java.util.Map;
import java.util.concurrent.TimeUnit;@Service
public class RedisServiceImpl implements IRedisService {@Autowiredprivate StringRedisTemplate stringRedisTemplate;@Autowiredprivate RedisTemplate redisTemplate;/*** @Description: 獲取自增長值 (每調用1次,ID自增1)* @param key key* @return*/public  Long getIncr(String key) {RedisAtomicLong entityIdCounter = new RedisAtomicLong(key, redisTemplate.getConnectionFactory());Long increment = entityIdCounter.getAndIncrement();//entityIdCounter.expire(0, TimeUnit.SECONDS);return increment;}/*** @Description: 初始化自增長值* @param key key* @param value 當前值*/public void setIncr(String key, int value) {RedisAtomicLong counter = new RedisAtomicLong(key, redisTemplate.getConnectionFactory());counter.set(value);//counter.expire(0, TimeUnit.SECONDS);}@Overridepublic void setValue(String key, Map<String, Object> value) {ValueOperations<String, Object> vo = redisTemplate.opsForValue();vo.set(key, value);redisTemplate.expire(key, 1, TimeUnit.HOURS); // 這里指的是1小時后失效}@Overridepublic Object getValue(String key) {ValueOperations<String, String> vo = redisTemplate.opsForValue();return vo.get(key);}@Overridepublic void setValue(String key, String value) {ValueOperations<String, Object> vo = redisTemplate.opsForValue();vo.set(key, value);redisTemplate.expire(key, 1, TimeUnit.HOURS); // 這里指的是1小時后失效}@Overridepublic void setValue(String key, Object value) {ValueOperations<String, Object> vo = redisTemplate.opsForValue();vo.set(key, value);redisTemplate.expire(key, 1, TimeUnit.HOURS); // 這里指的是1小時后失效}@Overridepublic Object getMapValue(String key) {ValueOperations<String, String> vo = redisTemplate.opsForValue();return vo.get(key);}}

其中包含了

getIncr
setIncr

這兩個方法可以用來設置、獲取自增長值。

4. sa-token 鑒權框架

用戶權限問題是一個系統不可缺少的部分,本項目采用sa-token框架。倒不是我給作者打廣告哈,而是我覺得確實很好用,shiro太重了,我更傾向于選擇sa-token,用我們國人自己的框架。

官網:Sa-Token

引入依賴:

<!-- Sa-Token 權限認證-->
<dependency><groupId>cn.dev33</groupId><artifactId>sa-token-spring-boot-starter</artifactId><version>1.28.0</version>
</dependency>

創建包

com.rabbit.diary.config

編寫

SaTokenConfigure
@Configuration
public class SaTokenConfigure implements WebMvcConfigurer {// 注冊Sa-Token的注解攔截器,打開注解式鑒權功能@Overridepublic void addInterceptors(InterceptorRegistry registry) {// 注冊注解攔截器,并排除不需要注解鑒權的接口地址 (與登錄攔截器無關)registry.addInterceptor(new SaAnnotationInterceptor()).addPathPatterns("/**");}
}

寫這個config是為了開啟sa-token注解。

5. 用戶接口和服務類

終于到用戶接口編寫啦,我們已經做了很多的準備工作,這個項目已經初步具備企業級工程化的標準了。很多企業小項目的架構雜亂無章,甚至都沒有這么完備的體系。

用戶服務類,我們采用接口+實現類的方式。

UserService
package com.rabbit.diary.service;import com.rabbit.diary.bean.User;
import org.springframework.stereotype.Service;public interface UserService {User getByUserName(String userName);void save(User user);
}
UserServiceImpl
package com.rabbit.diary.service.impl;import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.rabbit.diary.bean.User;
import com.rabbit.diary.dao.UserMapper;
import com.rabbit.diary.redis.RedisServiceImpl;
import com.rabbit.diary.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import java.lang.annotation.Annotation;
import java.util.List;@Service
public class UserServiceImpl implements UserService {@AutowiredUserMapper userMapper;@AutowiredRedisServiceImpl redisServiceImpl;/*** 根據用戶名獲取用戶* @param userName* @return*/@Overridepublic User getByUserName(String userName) {List<User> users = userMapper.selectList(new QueryWrapper<User>().eq("user_name", userName));if(users.size() > 0){return users.get(0);}return null;}/*** 保存用戶* @param user*/@Overridepublic void save(User user) {userMapper.insert(user);}}

很簡單吧,就兩個方法。

UserController:

?

package com.rabbit.diary.web;import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import com.rabbit.diary.bean.User;
import com.rabbit.diary.bean.core.BizException;
import com.rabbit.diary.bean.core.ExceptionCodeEnum;
import com.rabbit.diary.bean.core.Result;
import com.rabbit.diary.redis.RedisServiceImpl;
import com.rabbit.diary.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;@RestController
@RequestMapping("/user")
public class UserController {@AutowiredUserService userService;@AutowiredRedisServiceImpl redisServiceImpl;String salt = "diary188";@RequestMapping("register")public Result register(@RequestBody User user){if(StrUtil.isEmpty(user.getUserName())){throw new BizException(ExceptionCodeEnum.ERROR_PARAM.setDesc("用戶名不允許為空!"));}if(StrUtil.isEmpty(user.getPassword())){throw new BizException(ExceptionCodeEnum.ERROR_PARAM.setDesc("密碼不允許為空!"));}//檢查用戶名是否重復if(userService.getByUserName(user.getUserName()) != null){throw new BizException(ExceptionCodeEnum.ERROR_PARAM.setDesc("用戶名"+user.getUserName()+"重復!"));}//拼裝userBeanuser.setUid(redisServiceImpl.getIncr("userId")); //redis自增IDuser.setPassword(SecureUtil.md5(user.getPassword() + salt));user.setCreateTime(DateUtil.now());user.setUpdateTime(DateUtil.now());userService.save(user);return Result.success();}@RequestMapping("login")public Result login(@RequestBody User user){User user1 = userService.getByUserName(user.getUserName());if(user1 == null){throw new BizException(ExceptionCodeEnum.ERROR_PARAM.setDesc("不存在的用戶名:" + user.getUserName()));}String password = SecureUtil.md5(user.getPassword() + salt);if(!user1.getPassword().equals(password)){throw new BizException(ExceptionCodeEnum.ERROR_PARAM.setDesc("賬號和密碼不匹配:" + user.getUserName()));}/**登錄維持ID* */StpUtil.login(user1.getUid(),"PC");return Result.success();}}

下面我們挑幾個需要注意的點

01. 如何實現redis自增長ID?

 user.setUid(redisServiceImpl.getIncr("userId")); //redis自增ID

02.如何保持登錄ID?

StpUtil.login(user1.getUid(),"PC");
PageController
@Controller
public class PageController {@RequestMapping("login")public String login(){return "login";}@RequestMapping("/")@SaCheckLoginpublic String index(){return "index";}
}

注意,我們給index加上了saCheckLogin注解,這表示進入這個方法必須得是登錄狀態。

index.jsp是項目首頁

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<c:set var="basePath" value="${pageContext.request.contextPath}"></c:set>
<html>
<head><title>Title</title><script src="${basePath}/layui/layui.all.js" charset="utf-8"></script><link rel="stylesheet" href="${basePath}/layui/css/layui.css" media="all">
</head>
<body>首頁
</body>
</html>

啟動項目,讓我們訪問:http://localhost/

看到:

{"code":"9999","message":"Token無效:3fd99f78-10aa-4690-bd83-5fae198d1b87","data":null}

?這表示sa-token的配置已經生效了。

現在,我們去注冊幾個賬號:

這就順利進入了index.jsp。

6. 如何下載源碼?

關注下方公眾號,回復“日記本”即可。我把每一節的源碼都單獨打包給你準備好了。

版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。

原文链接:https://808629.com/144050.html

发表评论:

本站为非赢利网站,部分文章来源或改编自互联网及其他公众平台,主要目的在于分享信息,版权归原作者所有,内容仅供读者参考,如有侵权请联系我们删除!

Copyright © 2022 86后生记录生活 Inc. 保留所有权利。

底部版权信息