参考网站
https://www.cnblogs.com/victorbu/tag/Spring%20Boot/default.html?page=2 【我的页面代码使用这个人的】
提示
为了方便,定义了一个基类controller---BaseController。
public class BaseController {
public static final String CONTENT_TYPE_FORMED = "application/x-www-form-urlencoded";
//异常处理
//ExceptionHandler解决未被controller层吸收的exception
@ExceptionHandler(Exception.class)
@ResponseStatus(HttpStatus.OK)
@ResponseBody
public Object handleException(HttpServletRequest request, Exception ex){
Map<String, Object> responseData = new HashMap<>();
if(ex instanceof BusinessException){
BusinessException businessException = (BusinessException) ex;
responseData = new HashMap<>();
responseData.put("errCode",businessException.getErrorCode());
responseData.put("errMsg",businessException.getErrMsg());
}else{
responseData.put("errCode", EmBusinessError.UNKNOWN_ERROR.getErrorCode());
responseData.put("errMsg",EmBusinessError.UNKNOWN_ERROR.getErrMsg());
}
return CommonReturnType.create(responseData,"fail");
}
}
后面的每个controller都继承它即可
验证码功能
在这个视频中,主要讲解了如何生成验证码,以及如何进行检验。(因为这门课程是基础课程,视频中没用到Redis什么的,只是简单的把验证码放到了session中。)代码如下
//用户获取短信接口
@RequestMapping(value = "/getotp", method = {RequestMethod.POST})
@ResponseBody
public CommonReturnType getOtp(@RequestParam(name = "telphone") String telephone) {
//按照一定规则生成验证码
Random random = new Random();
int randonInt = random.nextInt(99999); //生成0~99999的数字
randonInt += 10000;
String otpCode = String.valueOf(randonInt);
//将OTP验证码通对应手机号关联。这里利用session
httpServletRequest.getSession().setAttribute(telephone, otpCode);
//发送短信
System.out.println("手机--->" + telephone + " 验证码---->:" + randonInt);
return CommonReturnType.create(null);
}
页面的话。我是用了别人写的页面。他是用vue + element UI来实现的。发送请求用的是基于vue的axios。不像原课程使用的boostrap和jQuery
<html>
<head>
<meta charset="UTF-8">
<title>获取验证码</title>
<link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
</head>
<body>
<div id="app">
<el-row>
<el-col :span="8" :offset="8">
<h3>获取 otp 信息</h3>
<el-form ref="form" :model="form" label-width="80px">
<el-form-item label="手机号">
<el-input v-model="form.telphone"></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="onSubmit">获取 otp 短信</el-button>
</el-form-item>
</el-form>
</el-col>
</el-row>
</div>
</body>
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://cdn.bootcss.com/axios/0.18.0/axios.min.js"></script>
<script src="https://unpkg.com/element-ui/lib/index.js"></script>
<script>
var app = new Vue({
el: '#app',
data: {
form: {
telphone: '',
}
},
methods: {
onSubmit(){
if(this.form.telphone == null || this.form.telphone == ''){
console.log('手机号不能为空');
alert('手机号不能为空')
return;
}
// https://www.cnblogs.com/yesyes/p/8432101.html
axios({
method: 'post',
url: 'http://localhost:8080/user/getotp',
data: this.form,
params: this.form,
headers: {'Content-Type': 'application/x-www-form-urlencoded'}
})
.then(resp=>{
if(resp.data.status == 'success'){
this.$message({
message: 'otp 已经发送到了您的手机上,请注意查收',
type: 'success'
});
}else{
this.$message.error('otp 发送失败,原因为:' + resp.data.data.errMsg);
}
})
.catch(err =>{
console.log();
this.$message.error('otp 发送失败,原因为:' + err.status + ', ' + err.statusText);
});
},
},
});
</script>
</html>
注册功能
页面绘制 register.html
在这里。我对属性名进行了修改。与controller代码对应,属性更好的完成封装
<html>
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
</head>
<body>
<div id="app">
<el-row>
<el-col :span="8" :offset="8">
<h3>用户注册</h3>
<el-form ref="user" :model="user" label-width="80px">
<el-form-item label="手机号">
<el-input v-model="user.telphone"></el-input>
</el-form-item>
<el-form-item label="验证码">
<el-input v-model="user.otpCode"></el-input>
</el-form-item>
<el-form-item label="昵称">
<el-input v-model="user.name"></el-input>
</el-form-item>
<el-form-item label="性别">
<el-switch
v-model="user.gender"
active-color="#ff4949"
inactive-color="#13ce66"
active-value="2"
inactive-value="1"
active-text="女"
inactive-text="男"></el-switch>
</el-form-item>
<el-form-item label="年龄">
<el-input v-model="user.age"></el-input>
</el-form-item>
<el-form-item label="密码">
<el-input v-model="user.encrptPassword" show-password></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="onSubmit">注册</el-button>
</el-form-item>
</el-form>
</el-col>
</el-row>
</div>
</body>
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://cdn.bootcss.com/axios/0.18.0/axios.min.js"></script>
<script src="https://unpkg.com/element-ui/lib/index.js"></script>
<script>
var app = new Vue({
el: '#app',
data: {
user: {
telphone: '',
otpCode: '',
name: '',
gender: 1,
age: 0,
encrptPassword: '',
}
},
methods: {
onSubmit(){
if(this.user.telphone == null || this.user.telphone == ''){
this.$message({
message: '手机号不能为空',
type: 'warning'
});
return;
}
// https://www.cnblogs.com/yesyes/p/8432101.html
axios({
method: 'post',
url: 'http://localhost:8080/user/register',
data: this.user,
params: this.user,
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
withCredentials: true,
})
.then(resp=>{
if(resp.data.status == 'success'){
this.$message({
message: '注册成功',
type: 'success'
});
}else{
this.$message.error('注册失败,原因为:' + resp.data.data.errMsg);
}
})
.catch(err =>{
this.$message.error('注册失败,原因为:' + err.status + ', ' + err.statusText);
});
},
},
});
</script>
</html>
pom.xml增加一个依赖。用他的StringUils方便判空
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.7</version>
</dependency>
Controller对应的代码
//用户注册接口
//这里修改了部分代码
@RequestMapping(value = "/register", method = {RequestMethod.POST})
@ResponseBody
public CommonReturnType register(UserModel user, @RequestParam(name = "otpCode") String otpCode) throws BusinessException, UnsupportedEncodingException, NoSuchAlgorithmException {
//验证手机号和对应的otpcode相符合
String inSessionOtpCode = (String) this.httpServletRequest.getSession().getAttribute(user.getTelphone());
if (StringUtils.equals(otpCode, inSessionOtpCode)) {
throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR, "短信验证码错误");
}
//注册流程
user.setRegisterMode("byPhone");
user.setEncrptPassword(EncodeNyMd5(user.getEncrptPassword()));
userService.register(user);
return CommonReturnType.create(null);
}
密码需要加密,加密的方法如下:
一开始视频使用自带的加密的,但是应该是自带的有点问题,所以重新封装了。
//md5加密
public String EncodeNyMd5(String str) throws NoSuchAlgorithmException, UnsupportedEncodingException {
//确定计算方法
MessageDigest md5 = MessageDigest.getInstance("MD5");
BASE64Encoder base64en = new BASE64Encoder();
//加密字符串
String newStr = base64en.encode(md5.digest(str.getBytes("utf-8")));
return newStr;
}
UserService接口增加方法
void register(UserModel userModel) throws BusinessException;
实现如下。
在这里,用insertSelective是因为有些数据是可以为空的。另外需要注意,手机不能重复。需要对数据库进行修改。由于我是用图形化界面,因此直接修改即可。
@Override
public void register(UserModel userModel) throws BusinessException {
if (userModel == null) {
throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR);
}
if (StringUtils.isEmpty(userModel.getName())
|| userModel.getGender() == null
|| userModel.getAge() == null
|| StringUtils.isEmpty(userModel.getTelphone())) {
throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR);
}
//实现 model -> dataobject方法
UserDO userDo = convertFromModel(userModel);
//这里手机号设置了不能重复。如果重复会有异常
try {
userDOMapper.insertSelective(userDo);
} catch (DuplicateKeyException e) {
throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR, "手机号码已注册");
}
userModel.setId(userDo.getId()); // 获取自增 id
UserPasswordDO userPasswordDO = convertPasswordFromModel(userModel);
userPasswordDOMapper.insertSelective(userPasswordDO);
}
由于注册是对2张表进行修改,所以要有事务处理。另外,mapper语句也要进行修改,方便获取自增后的主键
<insert id="insertSelective" parameterType="com.gpnu.miaoshaproject.dataobject.UserDO" keyProperty="id" useGeneratedKeys="true">
在注册时,会进行验证码的比对。但是由于跨域访问,session内容不共享,因此需要进行修改。前端页面也要做对应修改。但是我前端页面是用别人的,因此修改略。
跨域注解:
@CrossOrigin(allowCredentials = "true", allowedHeaders = "*") // 解决跨域问题
登录操作
页面绘制 Login.html
<html>
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
</head>
<body>
<div id="app">
<el-row>
<el-col :span="8" :offset="8">
<h3>用户登陆</h3>
<el-form ref="form" :model="form" label-width="80px">
<el-form-item label="手机号">
<el-input v-model="form.telphone"></el-input>
</el-form-item>
<el-form-item label="密码">
<el-input v-model="form.password" show-password></el-input>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="onSubmit">登陆</el-button>
<el-button @click="register">注册</el-button>
</el-form-item>
</el-form>
</el-col>
</el-row>
</div>
</body>
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://cdn.bootcss.com/axios/0.18.0/axios.min.js"></script>
<script src="https://unpkg.com/element-ui/lib/index.js"></script>
<script>
var app = new Vue({
el: '#app',
data: {
form: {
telphone: '',
password: '',
}
},
methods: {
onSubmit(){
if(this.form.telphone == null || this.form.telphone == ''){
this.$message({
message: '手机号不能为空',
type: 'warning'
});
return;
}
// https://www.cnblogs.com/yesyes/p/8432101.html
axios({
method: 'post',
url: 'http://localhost:8080/user/login',
data: this.form,
params: this.form,
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
withCredentials: true,
})
.then(resp=>{
if(resp.data.status == 'success'){
this.$message({
message: '登陆成功',
type: 'success'
});
}else{
this.$message.error('登陆失败,原因为:' + resp.data.data.errMsg);
}
})
.catch(err =>{
this.$message.error('登陆失败,原因为:' + err.status + ', ' + err.statusText);
});
},
register(){
window.location.href='getotp.html';
},
},
});
</script>
</html>
userService接口添加方法
UserModel validateLogin(String telphone, String encrptPassword) throws BusinessException;
实现如下:
@Override
public UserModel validateLogin(String telphone, String encrptPassword) throws BusinessException {
//先查出该用户信息
UserDO userDO = userDOMapper.selectByTelphone(telphone);
if (userDO == null) {
throw new BusinessException(EmBusinessError.USER_LOGIN_FAIL);
}
//查找密码
UserPasswordDO userPasswordDO = userPasswordDOMapper.selectByUserId(userDO.getId());
UserModel userModel = convertFromDataObject(userDO, userPasswordDO);
System.out.println(userModel.getEncrptPassword());
//检查是否相等
if (!StringUtils.equals(encrptPassword, userModel.getEncrptPassword())) {
throw new BusinessException(EmBusinessError.USER_LOGIN_FAIL);
}
return userModel;
}
大体思路:根据传过来的电话号码,来查出用户的信息。如果信息为空,则提示用户。如果信息不为空,则对用户输入的密码与数据库密码进行比对。
由于根据电话号码查找出id,再查找密码。因此需要在UserDOMapper增加方法
UserDO selectByTelphone(String telphone);
sql代码如下:
<select id="selectByTelphone" parameterType="java.lang.String" resultMap="BaseResultMap">
select
<include refid="Base_Column_List" />
from user_info
where telphone = #{telphone,jdbcType=VARCHAR}
</select>
Controller代码
//登录
@RequestMapping(value = "/login", method = {RequestMethod.POST})
@ResponseBody
public CommonReturnType checkLogin(@RequestParam(name = "telphone") String telphone,@RequestParam(name = "password") String password) throws BusinessException, UnsupportedEncodingException, NoSuchAlgorithmException {
//判空
if(StringUtils.isEmpty(telphone) || StringUtils.isEmpty(password)){
throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR);
}
//将用户输入的密码进行加密并传过去验证
UserModel userModel = userService.validateLogin(telphone, EncodeNyMd5(password));
//保存登录状态
httpServletRequest.getSession().setAttribute("LOGIN", true);
httpServletRequest.getSession().setAttribute("LOGIN_USER", userModel);
return CommonReturnType.create(null);
}
总结
在写的过程中,遇到了一个问题,总是登录失败,而且信息提示未知错误。后面debug才发现是之前的sql语句错了,查不出对应的人的密码。导致封装失败,因此报未知错误。
在这几个视频中,学会了解决跨域访问。