说明:
(1)本篇博客内容:开发【管理员登录】接口;
目录
一:管理员账户(包括admin_user表),简述;
1.admin_user表,简介;
2.直接去数据库,手动创建一个admin管理员账户;
二:开发管理员登录:Dao、Service部分;
1.根据admin_user表,生成【实体类、mapper接口、mapper.xml】;
2.在【imooc-news-dev-service-admin】管理服务中,创建管理员登录的Service部分;
(1)创建AdminUserService接口,定义【根据"管理员用户名",查询管理员用户】的方法;
(2)创建AdminUserServiceImpl实现类,去实现【根据"管理员用户名",查询管理员用户】方法;
三:开发管理员登录:Controller部分;
1.首先,在【api】接口工程中,定义AdminMngControllerApi接口,并定义【管理员登录】接口;
2.然后,在【admin】管理服务工程中,定义AdminMngController类,去实现AdminMngControllerApi接口;实现【管理员登录】接口;
四:效果;
一:管理员账户(包括admin_user表),简述;
1.admin_user表,简介;
针对admin用户,我们单独创建了一张表,而没有和普通用户公用一张表;
说明:
(1)MongoDB中有GridFS(GridFS是一种将大型文件存储在MongoDB的文件规范);我们的人脸入库信息,也是一个文件,我们可以把这个文件上传到MongoDB中去
(2)admin_name管理人员的姓名,一般需要是真实姓名;
(3)admin后台管理服务,没有注册功能;而是有一个"账号预分配"的概念;
● 即,我们会手动创建admin账号,然后再通过这个admin账号再去创建其他的admin账号;
● 所以,在下一部分,我们按照我们这个项目的"管理员用户的密码的加密逻辑",直接去数据库手动创建一个admin账号;
针对admin用户,我们单独创建了一张表,而没有和普通用户公用一张表;
说明:
(1)MongoDB中有GridFS(GridFS是一种将大型文件存储在MongoDB的文件规范);我们的人脸入库信息,也是一个文件,我们可以把这个文件上传到MongoDB中去
(2)admin_name管理人员的姓名,一般需要是真实姓名;
(3)admin后台管理服务,没有注册功能;而是有一个"账号预分配"的概念;
● 即,我们会手动创建admin账号,然后再通过这个admin账号再去创建其他的admin账号;
● 所以,在下一部分,我们按照我们这个项目的"管理员用户的密码的加密逻辑",直接去数据库手动创建一个admin账号;
2.直接去数据库,手动创建一个admin管理员账户;
(1)以前,自己使用过的密码加密方式是MD5加密;可以参考【Spring Boot电商项目18:用户模块七:使用MD5,对密码进行保护;】;这篇博客,也总结了以前遇到过的所有使用MD5加密的情况;
(2)这儿我们采用的加密方式是密码哈希;有关密码哈希的内容,可以先参考【附加:MD5算法原理;BCrypt密码哈希;】;
然后,可以在程序中,单独创建一个类;这个类的作用,就是加密密码的;
package com.imooc.admin.controller;
import org.springframework.security.crypto.bcrypt.BCrypt;
public class PWDTest {
public static void main(String[] args) {
String pwd = BCrypt.hashpw("admin", BCrypt.gensalt());
System.out.println(pwd);
}
}
说明:
(1)我们需要使用"Spring Security安全框架"中的【BCrypt.hashpw()工具类】(这个工具类是【org.springframework.security.crypto.bcrypt.BCrypt】包下的);为此,我们在【api】接口工程中,引入了【spring-cloud-starter-openfeign】;
● 在【spring-cloud-starter-openfeign】中,是有【org.springframework.security.crypto.bcrypt.BCrypt】包的;
(2)看注释;
(3)运行上面的代码,我们会得到一个字符串;
(4)至于,用户使用【admin,admin】登录的时候,密码是如何处理的,在本篇博客的后面会介绍;
(1)以前,自己使用过的密码加密方式是MD5加密;可以参考【Spring Boot电商项目18:用户模块七:使用MD5,对密码进行保护;】;这篇博客,也总结了以前遇到过的所有使用MD5加密的情况;
(2)这儿我们采用的加密方式是密码哈希;有关密码哈希的内容,可以先参考【附加:MD5算法原理;BCrypt密码哈希;】;
然后,可以在程序中,单独创建一个类;这个类的作用,就是加密密码的;
package com.imooc.admin.controller;
import org.springframework.security.crypto.bcrypt.BCrypt;
public class PWDTest {
public static void main(String[] args) {
String pwd = BCrypt.hashpw("admin", BCrypt.gensalt());
System.out.println(pwd);
}
}
说明:
(1)我们需要使用"Spring Security安全框架"中的【BCrypt.hashpw()工具类】(这个工具类是【org.springframework.security.crypto.bcrypt.BCrypt】包下的);为此,我们在【api】接口工程中,引入了【spring-cloud-starter-openfeign】;
● 在【spring-cloud-starter-openfeign】中,是有【org.springframework.security.crypto.bcrypt.BCrypt】包的;
(2)看注释;
(3)运行上面的代码,我们会得到一个字符串;
(4)至于,用户使用【admin,admin】登录的时候,密码是如何处理的,在本篇博客的后面会介绍;
二:开发管理员登录:Dao、Service部分;
这个【http://admin.imoocnews.com:8080/imooc-news/admin/login.html】就是后台系统管理员用户的登录页:
然后,我们接下来,就是开发管理员登录功能;
这个【http://admin.imoocnews.com:8080/imooc-news/admin/login.html】就是后台系统管理员用户的登录页:
然后,我们接下来,就是开发管理员登录功能;
1.根据admin_user表,生成【实体类、mapper接口、mapper.xml】;
利用【15:第二章:架构后端项目:11:使用【mybatis-generator-core】依赖生成实体类、mapper接口、mapper.xml等逆向文件;】中,创建的【mybatis-generator-database】工程;去针对admin_user表,生成【实体类、mapper接口、mapper.xml】;
(1)创建【generatorConfig-admin.xml】,并创建对应的包;
(2)创建AdminGenerator工具类,去根据【generatorConfig-admin.xml】生成【实体类、mapper接口、mapper.xml】;
(3)然后,把根据admin_user表生成的【实体类、mapper接口、mapper.xml】,copy到【admin】管理服务对应的目录中去;
● 把pojo实体类,放到【model】中;
● 把mapper接口、mapper.xml,放到【admin】中;
利用【15:第二章:架构后端项目:11:使用【mybatis-generator-core】依赖生成实体类、mapper接口、mapper.xml等逆向文件;】中,创建的【mybatis-generator-database】工程;去针对admin_user表,生成【实体类、mapper接口、mapper.xml】;
(1)创建【generatorConfig-admin.xml】,并创建对应的包;
(2)创建AdminGenerator工具类,去根据【generatorConfig-admin.xml】生成【实体类、mapper接口、mapper.xml】;
(3)然后,把根据admin_user表生成的【实体类、mapper接口、mapper.xml】,copy到【admin】管理服务对应的目录中去;
● 把pojo实体类,放到【model】中;
● 把mapper接口、mapper.xml,放到【admin】中;
2.在【imooc-news-dev-service-admin】管理服务中,创建管理员登录的Service部分;
(1)创建AdminUserService接口,定义【根据"管理员用户名",查询管理员用户】的方法;
package com.imooc.admin.service; import com.imooc.pojo.AdminUser; public interface AdminUserService { public AdminUser queryAdminByUsername(String username); }
(2)创建AdminUserServiceImpl实现类,去实现【根据"管理员用户名",查询管理员用户】方法;
package com.imooc.admin.service.impl; import com.imooc.admin.mapper.AdminUserMapper; import com.imooc.admin.service.AdminUserService; import com.imooc.pojo.AdminUser; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import tk.mybatis.mapper.entity.Example; @Service public class AdminUserServiceImpl implements AdminUserService { @Autowired public AdminUserMapper adminUserMapper; @Override public AdminUser queryAdminByUsername(String username) { //1.利用tkmybatis的Example,自定义查询条件; // 1.1先创建一个查询实例,这个实例,是针对AdminUser作查询的; Example adminExample = new Example(AdminUser.class); // 1.2给上面的查询实例,增加查询条件; Example.Criteria criteria = adminExample.createCriteria(); //第一个参数:AdminUser实体类中的username;第二个参数:queryAdminByUsername这个方法的username参数; criteria.andEqualTo("username", username); //2.利用创建好的查询条件,调用方法,去查数据库; AdminUser adminUser = adminUserMapper.selectOneByExample(adminExample); return adminUser; } }说明:
(1)需要注入AdminUserMapper接口对象;
(2)在【26:第三章:开发通行证服务:9:【注册/登录】接口】中,我们就利用过tkmybatis的Example,自定义查询条件;然后,去查询数据库;如果有不明白的,可以去参考;
(3)注意:selectOneByExample()方法中,参数是Example adminExample,而不是criteria;别写错了;
三:开发管理员登录:Controller部分;
1.首先,在【api】接口工程中,定义AdminMngControllerApi接口,并定义【管理员登录】接口;
AdminMngControllerApi接口:
package com.imooc.api.controller.admin;
import com.imooc.bo.AdminLoginBO;
import com.imooc.bo.UpdateUserInfoBO;
import com.imooc.grace.result.GraceJSONResult;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
@Api(value = "管理员admin维护相关Controller",tags = {"管理员admin维护相关Controller"})
@RequestMapping("adminMng") //设置路由,这个是需要前后端约定好的;
public interface AdminMngControllerApi {
@ApiOperation(value = "管理员用户登录", notes = "管理员用户登录", httpMethod = "POST")
@PostMapping("/adminLogin") //设置路由,这个是需要前后端约定好的;
public GraceJSONResult adminLogin(@RequestBody @Valid AdminLoginBO adminLoginBO,
BindingResult result,
HttpServletRequest request,
HttpServletResponse response);
}
说明:
(1)接口的url、参数类型、请求方式,都是前后端约定好的,别瞎写;
(2)然后,我们在【model】模型工程中,创建了AdminLoginBO类,来承接用户在前端输入的用户名和密码;
● 自然,我们使用@Valid开启了参数校验;
● 同时,一旦校验失败,有错误信息后,我们就可以通过【BindingResult result】来获取对应的错误信息;所以,这儿在参数中,也引入了【BindingResult result】;
● 这儿的内容,在【30:第三章:开发通行证服务:13:开发【更改/完善用户信息,接口】;】等博客中也有介绍;如有需要,可以去参考;
AdminMngControllerApi接口:
package com.imooc.api.controller.admin; import com.imooc.bo.AdminLoginBO; import com.imooc.bo.UpdateUserInfoBO; import com.imooc.grace.result.GraceJSONResult; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.springframework.validation.BindingResult; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.validation.Valid; @Api(value = "管理员admin维护相关Controller",tags = {"管理员admin维护相关Controller"}) @RequestMapping("adminMng") //设置路由,这个是需要前后端约定好的; public interface AdminMngControllerApi { @ApiOperation(value = "管理员用户登录", notes = "管理员用户登录", httpMethod = "POST") @PostMapping("/adminLogin") //设置路由,这个是需要前后端约定好的; public GraceJSONResult adminLogin(@RequestBody @Valid AdminLoginBO adminLoginBO, BindingResult result, HttpServletRequest request, HttpServletResponse response); }说明:
(1)接口的url、参数类型、请求方式,都是前后端约定好的,别瞎写;
(2)然后,我们在【model】模型工程中,创建了AdminLoginBO类,来承接用户在前端输入的用户名和密码;
● 自然,我们使用@Valid开启了参数校验;
● 同时,一旦校验失败,有错误信息后,我们就可以通过【BindingResult result】来获取对应的错误信息;所以,这儿在参数中,也引入了【BindingResult result】;
● 这儿的内容,在【30:第三章:开发通行证服务:13:开发【更改/完善用户信息,接口】;】等博客中也有介绍;如有需要,可以去参考;
2.然后,在【admin】管理服务工程中,定义AdminMngController类,去实现AdminMngControllerApi接口;实现【管理员登录】接口;
AdminMngController类:
package com.imooc.admin.controller;
import com.imooc.admin.service.AdminUserService;
import com.imooc.api.BaseController;
import com.imooc.api.controller.admin.AdminMngControllerApi;
import com.imooc.bo.AdminLoginBO;
import com.imooc.grace.result.GraceJSONResult;
import com.imooc.grace.result.ResponseStatusEnum;
import com.imooc.pojo.AdminUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.bcrypt.BCrypt;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;
import java.util.UUID;
@RestController
public class AdminMngController extends BaseController implements AdminMngControllerApi {
@Autowired
private AdminUserService adminUserService;
@Override
public GraceJSONResult adminLogin(AdminLoginBO adminLoginBO,
BindingResult result,
HttpServletRequest request,
HttpServletResponse response) {
//0.判断BindingResult中是否保存了验证失败的错误信息,如果有,说明前端的输入是有问题的(用户名或者密码,至少有一个没输入);
// 那么,我们就获取这个错误信息,并构建一个GraceJSONResult统一返回对象,返回;
if (result.hasErrors()) {
Map map = getErrorsFromBindingResult(result);
return GraceJSONResult.errorMap(map);
}
// 1.调用Service层的方法,根据用户名,尝试去数据库中查,看是否有这个用户;
AdminUser adminUser = adminUserService.queryAdminByUsername(adminLoginBO.getUsername());
// 2.判断adminUser是否为空;如果为空,则表示没有这个用户;
if (adminUser == null) {
//返回一个包含"管理员不存在或密码错误!"信息的,GraceJSONResult;
return GraceJSONResult.errorCustom(ResponseStatusEnum.ADMIN_NOT_EXIT_ERROR);
}
boolean isPwdMatch = BCrypt.checkpw(adminLoginBO.getPassword(), adminUser.getPassword());
//如果密码是OK的,直接返回OK的GraceJSONResult;
if (isPwdMatch) {
//首先,调用doLoginSettings方法,去把token信息保存到redis;token,id,name信息设置进cookie;
doLoginSettings(adminUser,request,response);
return GraceJSONResult.ok();
} else {//如果密码不OK,
//返回一个包含"管理员不存在或密码错误!"信息的,GraceJSONResult;
return GraceJSONResult.errorCustom(ResponseStatusEnum.ADMIN_NOT_EXIT_ERROR);
}
}
private void doLoginSettings(AdminUser adminUser,
HttpServletRequest request,
HttpServletResponse response) {
// 1.将token存放到redis中;
//用户会话,可以使用一个token;然后,token可以使用一个随机的uuid来做;
String token = UUID.randomUUID().toString();
//把该用户的token信息,保存在redis中;其key是【redis_admin_token:1001】这种;
redisOperator.set(REDIS_ADMIN_TOKEN + ":" + adminUser.getId(), token);
setCookie(request, response, "atoken", token, COOKIE_MONTH);
setCookie(request, response, "aid", adminUser.getId(), COOKIE_MONTH);
setCookie(request, response, "aname", adminUser.getAdminName(), COOKIE_MONTH);
}
}
说明:
(1)首先,校验参数是否OK;如果不OK,就从BindingResult中提取出错误信息,直接返回对应的GraceJSONResult;
● 以前在【25:第三章:开发通行证服务:8:【注册/登录】接口:接收并校验“手机号和验证码”参数;】等博客中,有过介绍;如果有需要,可以去参考;
(2)向redis中保存token,设置cookie;
● 以前在【27:第三章:开发通行证服务:10:【注册/登录】接口:注册/登录OK后,把用户会话信息(uid,utoken)保存到redis和cookie中;】中,有过介绍;如果有需要,可以去参考;
(3)BCrypt.checkpw()方法,会自动核验密码是否OK;
(4)我们在BaseController中,定义了REDIS_ADMIN_TOKEN;
AdminMngController类:
package com.imooc.admin.controller;
import com.imooc.admin.service.AdminUserService;
import com.imooc.api.BaseController;
import com.imooc.api.controller.admin.AdminMngControllerApi;
import com.imooc.bo.AdminLoginBO;
import com.imooc.grace.result.GraceJSONResult;
import com.imooc.grace.result.ResponseStatusEnum;
import com.imooc.pojo.AdminUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.bcrypt.BCrypt;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;
import java.util.UUID;
@RestController
public class AdminMngController extends BaseController implements AdminMngControllerApi {
@Autowired
private AdminUserService adminUserService;
@Override
public GraceJSONResult adminLogin(AdminLoginBO adminLoginBO,
BindingResult result,
HttpServletRequest request,
HttpServletResponse response) {
//0.判断BindingResult中是否保存了验证失败的错误信息,如果有,说明前端的输入是有问题的(用户名或者密码,至少有一个没输入);
// 那么,我们就获取这个错误信息,并构建一个GraceJSONResult统一返回对象,返回;
if (result.hasErrors()) {
Map map = getErrorsFromBindingResult(result);
return GraceJSONResult.errorMap(map);
}
// 1.调用Service层的方法,根据用户名,尝试去数据库中查,看是否有这个用户;
AdminUser adminUser = adminUserService.queryAdminByUsername(adminLoginBO.getUsername());
// 2.判断adminUser是否为空;如果为空,则表示没有这个用户;
if (adminUser == null) {
//返回一个包含"管理员不存在或密码错误!"信息的,GraceJSONResult;
return GraceJSONResult.errorCustom(ResponseStatusEnum.ADMIN_NOT_EXIT_ERROR);
}
boolean isPwdMatch = BCrypt.checkpw(adminLoginBO.getPassword(), adminUser.getPassword());
//如果密码是OK的,直接返回OK的GraceJSONResult;
if (isPwdMatch) {
//首先,调用doLoginSettings方法,去把token信息保存到redis;token,id,name信息设置进cookie;
doLoginSettings(adminUser,request,response);
return GraceJSONResult.ok();
} else {//如果密码不OK,
//返回一个包含"管理员不存在或密码错误!"信息的,GraceJSONResult;
return GraceJSONResult.errorCustom(ResponseStatusEnum.ADMIN_NOT_EXIT_ERROR);
}
}
private void doLoginSettings(AdminUser adminUser,
HttpServletRequest request,
HttpServletResponse response) {
// 1.将token存放到redis中;
//用户会话,可以使用一个token;然后,token可以使用一个随机的uuid来做;
String token = UUID.randomUUID().toString();
//把该用户的token信息,保存在redis中;其key是【redis_admin_token:1001】这种;
redisOperator.set(REDIS_ADMIN_TOKEN + ":" + adminUser.getId(), token);
setCookie(request, response, "atoken", token, COOKIE_MONTH);
setCookie(request, response, "aid", adminUser.getId(), COOKIE_MONTH);
setCookie(request, response, "aname", adminUser.getAdminName(), COOKIE_MONTH);
}
}
说明:
(1)首先,校验参数是否OK;如果不OK,就从BindingResult中提取出错误信息,直接返回对应的GraceJSONResult;
● 以前在【25:第三章:开发通行证服务:8:【注册/登录】接口:接收并校验“手机号和验证码”参数;】等博客中,有过介绍;如果有需要,可以去参考;
(2)向redis中保存token,设置cookie;
● 以前在【27:第三章:开发通行证服务:10:【注册/登录】接口:注册/登录OK后,把用户会话信息(uid,utoken)保存到redis和cookie中;】中,有过介绍;如果有需要,可以去参考;
(3)BCrypt.checkpw()方法,会自动核验密码是否OK;
(4)我们在BaseController中,定义了REDIS_ADMIN_TOKEN;
四:效果;
(1)先全局install一下整个项目;
(2)然后,启动前端项目;记得使用SwitchHost开启虚拟域名映射;
(3)启动【admin】管理服务的主启动类;
(4)访问【http://admin.imoocnews.com:8080/imooc-news/admin/login.html】后台系统管理员用户的登录页:
● 如果报了【There is no getter for property named ‘distinct‘ in ‘class tk.mybatis.mapper】错误,那么大概率是selectOneByExample()方法的参数,写错了;;;可以参考【There is no getter for property……】;
然后,如果输入密码admin的话,其OK了;
(1)先全局install一下整个项目;
(2)然后,启动前端项目;记得使用SwitchHost开启虚拟域名映射;
(3)启动【admin】管理服务的主启动类;
(4)访问【http://admin.imoocnews.com:8080/imooc-news/admin/login.html】后台系统管理员用户的登录页:
● 如果报了【There is no getter for property named ‘distinct‘ in ‘class tk.mybatis.mapper】错误,那么大概率是selectOneByExample()方法的参数,写错了;;;可以参考【There is no getter for property……】;
然后,如果输入密码admin的话,其OK了;



