168zwq
2023/05/09阅读:108主题:默认主题
springboot+vue3实现《个人小博客》登录注册(带验证码)功能
案例目录 《个人小博客》
1、登录注册(带验证码)
2、发布小文章(添加文章)
3、文章管理(删改查)
项目技术栈介绍
前端:vue3+elementplus
后端:springboot+mybatisplus
jdk:1.8以上
数据库:mysql
演示开始
前端部分
第一:前端部分(注册模块效果图)
实现代码也非常简单
html代码:来自register.vue
<p class="title">欢迎注册柠檬博客平台</p>
<el-form ref="formRef" :rules="rules" :model="formLabelAlign" label-width="90px">
<el-form-item prop="username" label="用户名">
<el-input ref="username" prefix-icon="el-icon-user" v-model.trim="formLabelAlign.username"
placeholder="请输入用户名" type="text" tabindex="1" autocomplete="off"></el-input>
</el-form-item>
<el-form-item prop="nickname" label="昵称">
<el-input ref="nickname" prefix-icon="el-icon-user" v-model.trim="formLabelAlign.nickname"
placeholder="请输入昵称" type="text" tabindex="1" autocomplete="off"></el-input>
</el-form-item>
<el-form-item prop="type" label="用户类型">
<el-select v-model="formLabelAlign.type" class="m-2" placeholder="选择用户类别">
<el-option v-for="item in options" :key="item.value" :label="item.label"
:value="item.value" />
</el-select>
</el-form-item>
<el-form-item prop="sex" label="性别">
<el-radio-group v-model="formLabelAlign.sex" class="ml-4" >
<el-radio v-for="item in SEX" :label="item.value" size="large">{{ item.label}}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item prop="password" label="密码">
<el-input ref="nepassword" prefix-icon="el-icon-lock" v-model.trim="formLabelAlign.password"
placeholder="请输入密码" type="text" tabindex="1" autocomplete="off"></el-input>
</el-form-item>
<el-form-item prop="nepassword" label="确认密码">
<el-input ref="usernepasswordname" prefix-icon="el-icon-lock" v-model.trim="formLabelAlign.nepassword"
placeholder="请输入密码" type="text" tabindex="1" autocomplete="off"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" style="width: 100%;"
@click.native.prevent="submitForm('formLabelAlign')"
@keyup.enter.native="submitForm('formLabelAlign')">注册</el-button>
</el-form-item>
</el-form>
js代码
const formRef = ref(null);
const submitForm = () => {
formRef.value.validate(async valid => {
if (valid) {
var param = {
userName: formLabelAlign.username,
password: formLabelAlign.password,
nickName:formLabelAlign.nickname,
sex:formLabelAlign.sex,
type:formLabelAlign.type
};
register(param).then(res => {
if(res.retCode==1){
router.push("/login");
}else{
ElMessage({
message: res.msg,
type: 'error',
})
}
});
} else {
return false;
}
});
};
document.onkeydown = function (e) {
if (window.event == undefined) {
var key = e.keyCode;
} else {
var key = window.event.keyCode;
}
if (key == 13) {
submitForm();
}
};
第二:前端部分(登陆模块效果图)

html代码:来自login.html
<el-form ref="formRef" :rules="rules" :model="formLabelAlign" label-width="70px">
<el-form-item prop="username" label="用户名">
<el-input
ref="username"
prefix-icon="el-icon-user"
v-model.trim="formLabelAlign.username"
placeholder="请输入用户名"
type="text"
tabindex="1"
autocomplete="off"
></el-input>
</el-form-item>
<el-form-item prop="password" label="密码">
<el-input
ref="username"
prefix-icon="el-icon-lock"
v-model.trim="formLabelAlign.password"
placeholder="请输入密码"
type="text"
tabindex="1"
autocomplete="off"
></el-input>
</el-form-item>
<el-form-item prop="urVerifiCode" label="验证码" class="itemcode">
<el-input
ref="username"
prefix-icon="el-icon-lock"
v-model.trim="formLabelAlign.urVerifiCode"
placeholder="验证码"
type="text"
tabindex="1"
style="width:100px;height: 40px;border-radius:none"
autocomplete="off"
></el-input>
<span class="icodes" @click="getIcodes">
<!--<img class="img" :src="this.newcodeurl+'busineCommon/users/get/imagecode?uuid='+this.uid" alt="" style="width:120px;height: 40px" />-->
<img class="img" :src="imgurl" alt="" style="width:120px;height: 40px" />
</span>
</el-form-item>
<el-form-item>
<el-button
type="primary"
style="width: 100%;"
@click.native.prevent="submitForm('formLabelAlign')"
@keyup.enter.native="submitForm('formLabelAlign')"
>登录</el-button>
</el-form-item>
<el-form-item>
<el-button
type="danger"
style="width: 100%;"
@click="RegIster"
>注册</el-button>
</el-form-item>
</el-form>
js代码:
const formLabelAlign = reactive({
username: "",
password: "",
urVerifiCode:''
});
**注意配置接口**
const getcodes=()=>{
getcode({}).then(res=>{
imgurl.value = window.URL.createObjectURL(res);
})
}
const getIcodes=()=>{
getcodes();
}
const submitForm = () => {
formRef.value.validate(async valid => {
if (valid) {
var param = {
userName: formLabelAlign.username,
password: formLabelAlign.password,
code:formLabelAlign.urVerifiCode
};
login(param).then(res => {
if(res.retCode==1){
var token = res.data.token;
var name = formLabelAlign.username;
var url=res.avatar;
res.data.userName=name;
store.commit("SET_USER", res.data);
store.commit("SET_TOKENS", token);
}else{
getcodes();
ElMessage({
message: res.msg,
type: 'error',
})
}
});
} else {
return false;
}
});
};
后端部分
开发步骤
-
数据库建表 user_info -
项目搭建 -
配置跨域 -
mybatisPlus配置 -
编写user_info的增删改查 -
配置swagger(接口文档) -
编写验证码接口 -
编写注册接口 -
编写登陆接口 -
编写注销接口 -
添加正则校验 -
登陆过期过滤器
数据库建表
创建user_info表,其中主键为id,设为自增。其中登陆账号user_name,登陆密码password,昵称nick_name,人员类型type以及公有字段createTime和updateTime不可为空。
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;
-- ----------------------------
-- Table structure for user_info
-- ----------------------------
DROP TABLE IF EXISTS `user_info`;
CREATE TABLE `user_info` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '自增主键',
`user_name` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL COMMENT '登陆用户名',
`password` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL COMMENT '登录密码',
`nick_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL COMMENT '昵称',
`email` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '邮箱',
`img_id` text CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL COMMENT '头像图片id',
`type` int(11) NOT NULL DEFAULT 1 COMMENT '人员类型 1-普通用户,2-管理员',
`sex` int(11) NOT NULL DEFAULT 0 COMMENT '性别 0-未知,1-男,2-女',
`phone_number` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL COMMENT '手机号',
`create_time` datetime(0) NOT NULL COMMENT '创建时间',
`update_time` datetime(0) NOT NULL COMMENT '修改时间',
`is_delete` int(11) NULL DEFAULT 0 COMMENT '是否删除 0-未删除,1-已删除',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin ROW_FORMAT = Dynamic;
SET FOREIGN_KEY_CHECKS = 1;

项目搭建
创建maven项目,随后在pom中引入Springboot、myabatisPlus、swagger、redis以及数据库连接池等依赖
pom.xml
<!--springboot-starter-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--springboot-web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--springboot-redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--swagger-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version> 2.9.2</version>
</dependency>
<!--swagger-ui-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
<!--mysql连接-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!--mybatisPlus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatisPlus.version}</version>
</dependency>
<!--druid连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>${druid.version}</version>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
在项目中加入两个需要用到的公用分页实体类
PageRequest.java
@Data
@ApiModel("分页请求参数")
public class PageRequest<T> implements Serializable {
//当前页(默认第一页)
@ApiModelProperty(value = "当前页码,不传默认为1")
private Integer pageNum = 1;
//每页大小(默认每页20条)
@ApiModelProperty(value = "每页条数,不传默认为20")
private Integer pageSize = 20;
//排序字段和方式
private List<Sort> sortBy;
//查询的数据
@ApiModelProperty(value = "查询的参数")
private T data;
@Data
public static class Sort{
private String sortFiled;
private String sort;
}
}
PageResult.java
@Data
@ApiModel("分页结果")
public class PageResult<T> {
//总条数
@ApiModelProperty(value = "结果总条数")
private Long total = 0L;
//总页数
@ApiModelProperty(value = "结果总页数")
private Integer totalPage;
//当前页数据
@ApiModelProperty(value = "当前页的数据集")
private List<T> data;
//当前页
@ApiModelProperty(value = "当前页")
private Integer pageNum;
//每页条数
@ApiModelProperty(value = "每页条数")
private Integer pageSize;
//是否为最后页
@ApiModelProperty(value = "是否为最后页")
private Boolean isLastPage = false;
}
application.properties中需要加入mysql等配置
#端口号
server.port=8081
#DB Configuration:
spring.datasource.driverClassName=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://xxxx:3306/xxxx?serverTimezone=GMT%2B8
useUnicode=true&characterEncoding=utf8
spring.datasource.username=xxxx
spring.datasource.password=xxxxx
配置跨域
因本系统采用前后端分离的方式开发,所以在后台需要配置请求跨域 WebConfig.java
@Configuration
public class WebConfig extends WebMvcConfigurationSupport {
//跨域配置
@Override
public void addCorsMappings(CorsRegistry registry) {
// 设置允许跨域的路径
registry.addMapping("/**")
// 设置允许跨域请求的域名
.allowedOrigins("*")
// 是否允许cookie
.allowCredentials(true)
// 设置允许的请求方式
.allowedMethods("GET", "POST", "DELETE", "PUT")
// 设置允许的header属性
.allowedHeaders("*");
}
}
添加mybatisPlus的配置
application.properties
#指定mapper中xml文件的位置
mybatis-plus.mapper-locations = classpath*:mapper/xml/*.xml
#mybatis中pojo的位置
mybatis-plus.type-aliases-package = com.demo.model
#mybatis的主键策略
mybatis-plus.global-config.db-config.id-type =auto
mybatis-plus.global-config.db-config.logic-delete-field=isDelete
mybatis-plus.global-config.db-config.logic-delete-value=1
mybatis-plus.global-config.db-config.logic-not-delete-value=0
因为项目中有createTime和updateTime字段,在一些添加和修改的接口中,为了避免每次都需要给这两个字段赋值,所以可以通过实现MetaObjectHandler后,重写其中的insertFill和updateFill方法,这样在每次有添加和修改的请求调用时,都会走入这个方法中对这两个字段进行赋值。在使用时后面需要在实体类中对这两个字段进行配置。
FillFieldConfiguration.java
@Component
public class FillFieldConfiguration implements MetaObjectHandler {
public FillFieldConfiguration() {
}
@Override
public void insertFill(MetaObject metaObject) {
Date date = new Date();
String[] setterNames = metaObject.getSetterNames();
for (String setterName : setterNames) {
if(setterName.equals("createTime") && getFieldValByName("createTime",metaObject) == null){
metaObject.setValue("createTime", date);
}
if(setterName.equals("updateTime") && getFieldValByName("updateTime",metaObject) == null){
metaObject.setValue("updateTime", date);
}
}
}
@Override
public void updateFill(MetaObject metaObject) {
Date date = new Date();
String[] setterNames = metaObject.getSetterNames();
for (String setterName : setterNames) {
if(setterName.equals("updateTime") && getFieldValByName("updateTime",metaObject) == null){
metaObject.setValue("updateTime", date);
}
}
}
}
user_info的增删改查
UserInfo.java
@Data
@EqualsAndHashCode(callSuper = false)
@TableName("user_info")
@ApiModel(value="user_info对象", description="")
public class UserInfo{
private static final long serialVersionUID = 1L;
@TableId(value = "id", type = IdType.AUTO)
@ApiModelProperty(value = "自增主键")
private Integer id;
@TableField("user_name")
@ApiModelProperty(value = "登陆用户名")
private String userName;
@TableField("password")
@ApiModelProperty(value = "登录密码")
private String password;
@TableField("nick_name")
@ApiModelProperty(value = "昵称")
private String nickName;
@TableField("email")
@ApiModelProperty(value = "邮箱")
private String email;
@TableField("img_id")
@ApiModelProperty(value = "头像图片id")
private String imgId;
@TableField("type")
@ApiModelProperty(value = "人员类型 1-普通用户,2-管理员")
private Integer type;
@TableField("sex")
@ApiModelProperty(value = "性别 0-未知,1-男,2-女")
private Integer sex;
@TableField("phone_number")
@ApiModelProperty(value = "手机号")
private String phoneNumber;
@TableField(value = "create_time",fill= FieldFill.INSERT)
@ApiModelProperty(value = "创建时间")
private Date createTime;
@TableField(value = "update_time",fill = FieldFill.INSERT_UPDATE)
@ApiModelProperty(value = "修改时间")
private Date updateTime;
@TableField("is_delete")
@ApiModelProperty(value = "是否删除 0-未删除,1-已删除")
private Integer isDelete;
}
创建vo和dto


UserInfoQueryDTO.java
@Getter
@Setter
public class UserInfoQueryDTO {
@ApiModelProperty("自增主键")
private Integer id;
@ApiModelProperty("登陆用户名")
private String userName;
@ApiModelProperty("登录密码")
private String password;
@ApiModelProperty("昵称")
private String nickName;
@ApiModelProperty("邮箱")
private String email;
@ApiModelProperty("头像图片id")
private String imgId;
@ApiModelProperty("人员类型 1-普通用户,2-管理员")
private Integer type;
@ApiModelProperty("性别 0-未知,1-男,2-女")
private Integer sex;
@ApiModelProperty("手机号")
private String phoneNumber;
@ApiModelProperty("创建时间")
private Date createTime;
@ApiModelProperty("修改时间")
private Date updateTime;
@ApiModelProperty("是否删除 0-未删除,1-已删除")
private Integer isDelete;
}
UserInfoVO.java
@Getter
@Setter
public class UserInfoVO {
@ApiModelProperty("自增主键")
private Integer id;
@ApiModelProperty("登陆用户名")
private String userName;
@ApiModelProperty("登录密码")
private String password;
@ApiModelProperty("昵称")
private String nickName;
@ApiModelProperty("邮箱")
private String email;
@ApiModelProperty("头像图片id")
private String imgId;
@ApiModelProperty("人员类型 1-普通用户,2-管理员")
private Integer type;
@ApiModelProperty("性别 0-未知,1-男,2-女")
private Integer sex;
@ApiModelProperty("手机号")
private String phoneNumber;
@ApiModelProperty("创建时间")
private Date createTime;
@ApiModelProperty("修改时间")
private Date updateTime;
@ApiModelProperty("是否删除 0-未删除,1-已删除")
private Integer isDelete;
}
UserInfoController.java
@RestController
@Api(tags = {""})
@RequestMapping("/userInfo")
@CrossOrigin(allowCredentials = "true")
public class UserInfoController {
@Autowired
private UserInfoService userInfoService;
@RequestMapping(value = "/queryPageList", method = RequestMethod.POST)
@ApiOperation("获取分页列表")
public ResultData<PageResult<UserInfoVO>> queryPageList(@RequestBody PageRequest<UserInfoQueryDTO> pageRequest) {
ResultData<PageResult<UserInfoVO>> pageResult = userInfoService.queryPageList(pageRequest);
return pageResult;
}
@RequestMapping(value = "/queryList", method = RequestMethod.POST)
@ApiOperation("获取列表")
public ResultData<List<UserInfoVO>> queryList(@RequestBody UserInfoQueryDTO dto) {
ResultData<List<UserInfoVO>> listResult = userInfoService.queryList(dto);
return listResult;
}
@RequestMapping(value = "/queryDetail", method = RequestMethod.POST)
@ApiOperation("获取详情")
public ResultData<UserInfoVO> queryDetail(@RequestParam String id) {
ResultData<UserInfoVO> detailResult = userInfoService.queryDetail(id);
return detailResult;
}
@RequestMapping(value = "/add", method = RequestMethod.POST)
@ApiOperation("新增")
public ResultData<String> add(@RequestBody @Valid UserInfoInsertDTO dto, BindingResult bindingResult) throws IOException {
if(bindingResult.hasErrors()){
return ResultData.error(bindingResult.getFieldError().getDefaultMessage());
}
return userInfoService.add(dto);
}
@RequestMapping(value = "/edit", method = RequestMethod.POST)
@ApiOperation("编辑")
public ResultData<String> edit(@RequestBody UserInfoUpdateDTO dto) {
return userInfoService.edit(dto);
}
@RequestMapping(value = "/deleteById", method = RequestMethod.POST)
@ApiOperation("删除")
public ResultData<String> deleteById(@RequestParam String id) {
return userInfoService.deleteById(id);
}
@RequestMapping(value = "/batchDeleteByIds", method = RequestMethod.POST)
@ApiOperation("批量删除")
public ResultData<String> batchDeleteByIds(@RequestParam List<String> ids) {
return userInfoService.batchDeleteByIds(ids);
}
}
UserInfoService.java
public interface UserInfoService {
// 获取分页列表
ResultData<PageResult<UserInfoVO>> queryPageList(PageRequest<UserInfoQueryDTO> pageRequest);
// 获取列表
ResultData<List<UserInfoVO>> queryList(UserInfoQueryDTO dto);
// 获取详情
ResultData<UserInfoVO> queryDetail(String id);
// 新增
ResultData<String> add(UserInfoInsertDTO dto);
// 编辑
ResultData<String> edit(UserInfoUpdateDTO dto);
// 根据id删除
ResultData<String> deleteById(String id);
// 批量根据id删除
ResultData<String> batchDeleteByIds(List<String> ids);
// 批量根据条件删除
ResultData<String> batchDelete(UserInfoDeleteDTO dto);
}
UserInfoServiceImpl.java
@Service
public class UserInfoServiceImpl extends ServiceImpl<UserInfoMapper, UserInfo> implements UserInfoService {
@Resource
private UserInfoMapper userInfoMapper;
@Override
public ResultData<PageResult<UserInfoVO>> queryPageList(PageRequest<UserInfoQueryDTO> pageRequest) {
Page<UserInfo> page = PageUtil.getPage(pageRequest);
QueryWrapper<UserInfo> queryWrapper = new QueryWrapper<>(BeanUtils.copyProperties(pageRequest.getData(), UserInfo.class));
IPage<UserInfoVO> resultDate = this.page(page, queryWrapper)
.convert(userInfo -> BeanUtils.copyProperties(userInfo, UserInfoVO.class));
PageResult<UserInfoVO> pageResult = PageUtil.getPageResult(resultDate);
return ResultData.success("查询成功", pageResult);
}
@Override
public ResultData<List<UserInfoVO>> queryList(UserInfoQueryDTO dto) {
UserInfo userInfo = BeanUtils.copyProperties(dto, UserInfo.class);
QueryWrapper<UserInfo> queryWrapper = new QueryWrapper<>(userInfo);
List<UserInfo> userInfoList = this.list(queryWrapper);
return ResultData.success("查询成功",BeanUtils.copyProperties(userInfoList, UserInfoVO.class));
}
@Override
public ResultData<UserInfoVO> queryDetail(String id) {
if(StringUtils.isBlank(id)){
return ResultData.error("传入参数为空!");
}
return ResultData.success("查询成功",BeanUtils.copyProperties(this.getById(id), UserInfoVO.class));
}
@Override
public ResultData<String> add(UserInfoInsertDTO dto) {
Boolean result = this.save(BeanUtils.copyProperties(dto,UserInfo.class));
if (!result) {
return ResultData.error("添加失败!");
}
return ResultData.success("添加成功!");
}
@Override
public ResultData<String> edit(UserInfoUpdateDTO dto) {
if(dto.getId() == null){
return ResultData.error("传入id不可为空!");
}
Boolean result = this.updateById(BeanUtils.copyProperties(dto,UserInfo.class));
if (!result) {
return ResultData.error("修改失败!");
}
return ResultData.success("修改成功!");
}
@Override
public ResultData<String> deleteById(String id) {
if(StringUtils.isBlank(id)){
return ResultData.error("传入参数为空!");
}
Boolean result = this.removeById(id);
if(!result){
return ResultData.error("删除失败!");
}
return ResultData.success("删除成功!");
}
@Override
public ResultData<String> batchDeleteByIds(List<String> ids) {
if(CollectionUtils.isEmpty(ids)){
return ResultData.error("传入参数为空!");
}
int result = userInfoMapper.deleteBatchIds(ids);
return ResultData.success("删除成功!删除了"+result+"条数据");
}
@Override
public ResultData<String> batchDelete(UserInfoDeleteDTO dto) {
UserInfo userInfo = BeanUtils.copyProperties(dto, UserInfo.class);
QueryWrapper<UserInfo> queryWrapper = new QueryWrapper<>(userInfo);
int result = userInfoMapper.delete(queryWrapper);
return ResultData.success("删除成功!删除了"+result+"条数据");
}
}
UserInfoMapper.java
@Repository
public interface UserInfoMapper extends BaseMapper<UserInfo> {
}
配置swagger
SwaggerConfig.java
@Configuration
@EnableSwagger2 //开启swagger2
public class SwaggerConfig {
//配置了swagger的Docket的bean实例
@Value("${controller-locations}")
private String url;
@Bean
public Docket docket(){
return new Docket(DocumentationType.SWAGGER_2)
.ignoredParameterTypes(HttpSession.class, HttpServletRequest.class, HttpServletResponse.class)
.apiInfo(apiInfo())
.select()
.apis(RequestHandlerSelectors.basePackage(url))
.paths(PathSelectors.any())
.build();
}
//配置swagger信息
private ApiInfo apiInfo(){
return new ApiInfoBuilder()
.title("XX的swagger文档")
.description("")
.build();
}
}
编写验证码接口
LoginController.java
@RestController
@CrossOrigin(allowCredentials = "true")
public class LoginController {
@Autowired
private RedisUtil redisUtil;
/**
* 得到登陆验证码
* @author libing
* @param response
* @throws IOException
*/
@RequestMapping(value = "getCode", method = RequestMethod.POST)
public void getCode(HttpServletResponse response) throws IOException {
//定义图形验证码的长和宽
LineCaptcha lineCaptcha = CaptchaUtil.createLineCaptcha(116, 36, 4, 5);
redisUtil.setEx(lineCaptcha.getCode(),lineCaptcha.getCode(),10, TimeUnit.MINUTES);
try {
ServletOutputStream outputStream = response.getOutputStream();
lineCaptcha.write(outputStream);
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
注册接口
RegisterController.java
@RestController
@CrossOrigin(allowCredentials = "true")
public class RegisterController {
@Autowired
private UserInfoService userInfoService;
@RequestMapping(value = "register", method = RequestMethod.POST)
@ApiOperation("注册")
public ResultData<String> register(@RequestBody @Valid RegisterDTO registerDTO, BindingResult bindingResult){
if(bindingResult.hasErrors()){
return ResultData.error(bindingResult.getFieldError().getDefaultMessage());
}
UserInfoQueryDTO userInfoQueryDTO = new UserInfoQueryDTO();
userInfoQueryDTO.setUserName(registerDTO.getUserName());
ResultData<List<UserInfoVO>> listResultData = userInfoService.queryList(userInfoQueryDTO);
if(!CollectionUtils.isEmpty(listResultData.getData())){
return ResultData.error("您已注册过,不能重复注册!");
}
UserInfoInsertDTO userInfoInsertDTO = BeanUtils.copyProperties(registerDTO, UserInfoInsertDTO.class);
ResultData<String> add = userInfoService.add(userInfoInsertDTO);
if(add.isFail()){
return ResultData.error(add.getMsg());
}
return ResultData.success("注册成功!");
}
}
登陆接口
LoginController.java
@RestController
@CrossOrigin(allowCredentials = "true")
public class LoginController {
@Autowired
private UserInfoService userInfoService;
@RequestMapping(value = "login", method = RequestMethod.POST)
@ApiOperation("登陆")
public ResultData<LoginVO> login(@RequestBody @Valid LoginDTO loginDTO, BindingResult bindingResult, HttpServletRequest request,HttpServletResponse response ){
if(bindingResult.hasErrors()){
return ResultData.error(bindingResult.getFieldError().getDefaultMessage());
}
String code = redisUtil.get(loginDTO.getCode());
if(StringUtils.isBlank(code)){
return ResultData.error("验证码不正确!");
}
//查询用户
UserInfoQueryDTO userInfoQueryDTO = new UserInfoQueryDTO();
userInfoQueryDTO.setUserName(loginDTO.getUserName());
ResultData<List<UserInfoVO>> listResultData = userInfoService.queryList(userInfoQueryDTO);
if(CollectionUtils.isEmpty(listResultData.getData())){
return ResultData.error("您尚未注册!");
}
List<UserInfoVO> data = listResultData.getData();
if(data.size()>1){
return ResultData.error("登陆不合法!");
}
UserInfoVO userInfoVO = data.get(0);
if (!loginDTO.getPassword().equals(userInfoVO.getPassword())) {
return ResultData.error("密码不正确!");
}
LoginVO loginVO = BeanUtils.copyProperties(userInfoVO, LoginVO.class);
String token = JwtUtil.createJWT(String.valueOf(loginVO.getId()));
redisUtil.setEx("login"+loginVO.getId(), JSON.toJSONString(loginVO),60,TimeUnit.MINUTES);
response.setHeader("token",token);
return ResultData.success("登陆成功",loginVO);
}
}
注销登录
LoginController.java
@RequestMapping(value = "unLogin", method = RequestMethod.POST)
@ApiOperation("注销登陆")
public ResultData<String> login(@RequestParam String id){
if(StringUtils.isBlank(id)){
return ResultData.error("用户id不能为空!");
}
redisUtil.delete("login"+id);
return ResultData.success("注销成功!");
}
添加正则校验
在注册时需要给电话号码,邮箱等字段进行合法性校验,所以需要加入正则校验
RegexUtils.java
/**
* 正则工具类
* @author zkx
*
*/
public class RegexUtils {
/**
* 验证Email
* @param email email地址,格式:zhangsan@sina.com,zhangsan@xxx.com.cn,xxx代表邮件服务商
* @return 验证成功返回true,验证失败返回false
*/
public static boolean checkEmail(String email) {
String regex = "\\w+@\\w+\\.[a-z]+(\\.[a-z]+)?";
return Pattern.matches(regex, email);
}
/**
* 验证身份证号码
* @param idCard 居民身份证号码18位,第一位不能为0,最后一位可能是数字或字母,中间16位为数字 \d同[0-9]
* @return 验证成功返回true,验证失败返回false
*/
public static boolean checkIdCard(String idCard) {
String regex = "[1-9]\\d{16}[a-zA-Z0-9]{1}";
return Pattern.matches(regex,idCard);
}
/**
* 验证手机号码(支持国际格式,+86135xxxx...(中国内地),+00852137xxxx...(中国香港))
* @param mobile 移动、联通、电信运营商的号码段
*<p>移动的号段:134(0-8)、135、136、137、138、139、147(预计用于TD上网卡)
*、150、151、152、157(TD专用)、158、159、187(未启用)、188(TD专用)</p>
*<p>联通的号段:130、131、132、155、156(世界风专用)、185(未启用)、186(3g)</p>
*<p>电信的号段:133、153、180(未启用)、189</p>
* @return 验证成功返回true,验证失败返回false
*/
public static boolean checkMobile(String mobile) {
String regex = "(\\+\\d+)?1[3456789]\\d{9}$";
return Pattern.matches(regex,mobile);
}
/**
* 验证固定电话号码
* @param phone 电话号码,格式:国家(地区)电话代码 + 区号(城市代码) + 电话号码,如:+8602085588447
* <p><b>国家(地区) 代码 :</b>标识电话号码的国家(地区)的标准国家(地区)代码。它包含从 0 到 9 的一位或多位数字,
* 数字之后是空格分隔的国家(地区)代码。</p>
* <p><b>区号(城市代码):</b>这可能包含一个或多个从 0 到 9 的数字,地区或城市代码放在圆括号——
* 对不使用地区或城市代码的国家(地区),则省略该组件。</p>
* <p><b>电话号码:</b>这包含从 0 到 9 的一个或多个数字 </p>
* @return 验证成功返回true,验证失败返回false
*/
public static boolean checkPhone(String phone) {
String regex = "(\\+\\d+)?(\\d{3,4}\\-?)?\\d{7,8}$";
return Pattern.matches(regex, phone);
}
}
在register接口中加入校验 RegisterController.java
if(StringUtils.isNotBlank(registerDTO.getPhoneNumber())){
if(!RegexUtils.checkMobile(registerDTO.getPhoneNumber())){
return ResultData.error("您输入的手机号码格式不正确!");
}
}
if(StringUtils.isNotBlank(registerDTO.getEmail())){
if(!RegexUtils.checkEmail(registerDTO.getEmail())){
return ResultData.error("您输入的邮箱格式不正确!");
}
}
登陆过期过滤器
TokenInterceptor.java
@Component
public class TokenInterceptor extends HandlerInterceptorAdapter {
@Autowired
private RedisUtil redisUtil;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String token = request.getHeader("token");
if(StringUtils.isNotBlank(token)){
//解析token
String userId;
try {
Claims claims = JwtUtil.parseJWT(token);
userId = claims.getSubject();
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("token非法");
}
//从redis中获取用户信息
String json = redisUtil.get("login" + userId);
if(StringUtils.isNotBlank(json)){
return true;
}
}
ResultData<Object> outTime = ResultData.outTime("身份验证失败");
response.getOutputStream().write(JSON.toJSONString(outTime).getBytes());
return false;
}
}
源码获取地址(以上前后端代码已经全部打包好了)
https://gitee.com/xuxiaofei1996/case-source-code.git
为了方便大家更好的学习,本平台经常分享一些完整的单个功能案例代码给大家去练习,如果本平台没有你要学习的功能案例,你可以联系小编,提供你的小需求给我,我安排我们这边的开发团队免费帮你完成你的案例。
作者介绍