参考资料
https://www.cnblogs.com/three-fighter/p/14332288.html
零、前端开发
略。之前有过。
一、后端开发
1、统一结果封装
创建了一个 Result 类,用于异步统一返回的结果封装。一般来说,结果里面有几个要素必要的
是否成功,可用 code 表示(如 200 表示成功,400 表示异常)
结果消息
结果数据
public class Result implements Serializable {
//响应码
private Integer code;
//信息
private String message;
//返回数据
private Object data;
public Result(Integer code, String message, Object data) {
this.code = code;
this.message = message;
this.data = data;
}
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
}
实际上由于响应码是固定的,code 属性应该是一个枚举值,这里作了一些简化。
2、登录业务实体类
为了接收前端登录的数据,我们这里创建了一个登录用的业务实体类(LoginDTO)
public class LoginDTO {
private String loginName;
private String password;
public String getLoginName() {
return loginName;
}
public void setLoginName(String loginName) {
this.loginName = loginName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
3、控制层
LoginController,进行业务响应:
package com.test.demo.controller;
import com.test.demo.dto.LoginDTO;
import com.test.demo.result.Result;
import com.test.demo.service.LoginService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
/**
*
*
* @RestController
* 在 Spring Boot 中,@Controller 注解是专门用于处理 Http 请求处理的,是以 MVC 为核心的设计思想的控制层。@RestController 则是 @Controller 的衍生注解。
* Spring Boot 本身就 Spring MVC 的简化版本。是在 Spring MVC 的基础上实现了自动配置,简化了开发人员开发过程。
*
*@Autowired
*
* 注解基于依赖注入(Dependency Injection,DI)的原理。在Spring容器中,所有的Bean都会被实例化和管理,它们之间的依赖关系由Spring容器来维护。当一个Bean依赖于另一个Bean时,可以使用@Autowired注解来告诉Spring容器自动装配这些依赖。
*
* @PostMapping
* 在 Spring Boot 中,使用 @PostMapping 注解非常简单,只需要将它添加到一个方法的定义上即可
*我们使用 @PostMapping 注解声明了一个方法 loginr(),这个方法用来处理客户端发送的 POST 请求,并将请求体中的数据转换为 loginDTO 对象(并返回)。
*
* */
@RestController
public class LoginController {
@Autowired
LoginService loginService;
@PostMapping(value = "/api/login")
public Result login(@RequestBody LoginDTO loginDTO){
return loginService.login(loginDTO);
}
@PostMapping(value = "/api/hello")
public Result hello(){
return new Result(200,"hello","world");
}
}
Spring Boot Web 开发@Controller @RestController 使用教程
4、业务层
业务层进行实际的业务处理。
LoginService: service接口
public interface LoginService {
public Result login(LoginDTO loginDTO);
}
LoginServiceImpl:service接口实现类
@Service
public class LoginServiceImpl implements LoginService {
// @Autowired 是idea的问题,@Mapper 这个注解是 Mybatis 提供的,而 @Autowried 注解是 Spring 提供的
@Autowired(required=false)
private UserMapper userMapper;
@Override
public Result login(LoginDTO loginDTO) {
//判断登录名和密码是否为空:
//StringUtils 方法的操作对象是 Java.lang.String 类型的对象,是 JDK 提供的 String 类型操作方法的补充
if (StringUtils.isEmpty(loginDTO.getLoginName())){
return new Result(400,"账号不能为空","");
}
if (StringUtils.isEmpty(loginDTO.getPassword())){
return new Result(400,"密码不能为空","");
}
//通过登录名查询用户
//wrapper.eq("实体类::查询字段", "条件值"); //相当于where条件
QueryWrapper<User> wrapper = new QueryWrapper();
wrapper.eq("login_name", loginDTO.getLoginName());
User uer=userMapper.selectOne(wrapper);
//比较密码
if (uer!=null&&uer.getPassword().equals(loginDTO.getPassword())){
LoginVO loginVO=new LoginVO();
loginVO.setId(uer.getId());
//这里token直接用一个uuid
//使用jwt的情况下,会生成一个jwt token,jwt token里会包含用户的信息
loginVO.setToken(UUID.randomUUID().toString());
loginVO.setUser(uer);
return new Result(200,"",loginVO);
}
return new Result(401,"登录失败","");
}
}
LoginVO
.登录返回接口实体类
数据包:vo
token:返回一段随机字符串
public class LoginVO implements Serializable {
private Integer id;
private String token;
private User user;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
}
一个登录功能完成了
登录功能完善
前面虽然实现了登录,但只是一个简单的登录跳转,实际上并不能对用户的登录状态进行判别,接下来我们进一步完善登录功能。
首先开始后端的开发。
1、后端开发
1.1、拦截器
创建 interceptor 包,包下新建拦截器 LoginInterceptor
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {
//从header中获取token
String token = request.getHeader("token");
//如果token为空
if (StringUtils.isBlank(token)) {
setReturn(response,400,"用户未登录,请先登录");
return false;
}
//在实际使用中还会:
// 1、校验token是否能够解密出用户信息来获取访问者
// 2、token是否已经过期
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
}
//返回json格式错误信息
private static void setReturn(HttpServletResponse response, Integer code, String msg) throws IOException {
HttpServletResponse httpResponse = (HttpServletResponse) response;
httpResponse.setHeader("Access-Control-Allow-Credentials", "true");
httpResponse.setHeader("Access-Control-Allow-Origin", HttpContextUtil.getOrigin());
//UTF-8编码
httpResponse.setCharacterEncoding("UTF-8");
response.setContentType("application/json;charset=utf-8");
Result result = new Result(code,msg,"");
ObjectMapper objectMapper = new ObjectMapper();
String json = objectMapper.writeValueAsString(result);
httpResponse.getWriter().print(json);
}
}
为了能给前端返回 json 格式的结果,这里还用到了一个工具类,新建 util 包,util 包下新建工具类 HttpContextUtil
public class HttpContextUtil {
public static HttpServletRequest getHttpServletRequest() {
return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
}
public static String getDomain() {
HttpServletRequest request = getHttpServletRequest();
StringBuffer url = request.getRequestURL();
return url.delete(url.length() - request.getRequestURI().length(), url.length()).toString();
}
public static String getOrigin() {
HttpServletRequest request = getHttpServletRequest();
return request.getHeader("Origin");
}
}
1.2、拦截器配置
拦截器创建完成之后,还需要进行配置。
@Configuration
public class DemoWebConfig implements WebMvcConfigurer {
/**
* 拦截器配置
*
* @param registry
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
//添加拦截器
registry.addInterceptor(new LoginInterceptor()).addPathPatterns("/api/**")
//放行路径,可以添加多个
.excludePathPatterns("/api/login");
}
}
1.3、跨域配置
在之前的后台接口,有一个注解@CrossOrigin ,这个注解是用来跨域的,每个接口都写一遍肯定是不太方便的,这里我们 创建跨域配置类并添加统一的跨域配置:
@Configuration
public class CorsConfig {
@Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration corsConfiguration = new CorsConfiguration();
//允许源,这里允许所有源访问,实际应用会加以限制
corsConfiguration.addAllowedOrigin("*");
//允许所有请求头
corsConfiguration.addAllowedHeader("*");
//允许所有方法
corsConfiguration.addAllowedMethod("*");
source.registerCorsConfiguration("/**", corsConfiguration);
return new CorsFilter(source);
}
}
2.、登录service
这样一来,后端就需要生成一个 token 返回给前端,所以更改 LoginServiceImpl 里的登录方法。
@Service
public class LoginServiceImpl implements LoginService {
// @Autowired 是idea的问题,@Mapper 这个注解是 Mybatis 提供的,而 @Autowried 注解是 Spring 提供的
@Autowired(required=false)
private UserMapper userMapper;
@Override
public Result login(LoginDTO loginDTO) {
//判断登录名和密码是否为空:
//StringUtils 方法的操作对象是 Java.lang.String 类型的对象,是 JDK 提供的 String 类型操作方法的补充
if (StringUtils.isEmpty(loginDTO.getLoginName())){
return new Result(400,"账号不能为空","");
}
if (StringUtils.isEmpty(loginDTO.getPassword())){
return new Result(400,"密码不能为空","");
}
//通过登录名查询用户
//wrapper.eq("实体类::查询字段", "条件值"); //相当于where条件
QueryWrapper<User> wrapper = new QueryWrapper();
wrapper.eq("login_name", loginDTO.getLoginName());
User uer=userMapper.selectOne(wrapper);
//比较密码
if (uer!=null&&uer.getPassword().equals(loginDTO.getPassword())){
LoginVO loginVO=new LoginVO();
loginVO.setId(uer.getId());
//这里token直接用一个uuid
//使用jwt的情况下,会生成一个jwt token,jwt token里会包含用户的信息
loginVO.setToken(UUID.randomUUID().toString());
loginVO.setUser(uer);
return new Result(200,"",loginVO);
}
return new Result(401,"登录失败","");
}
}
其中对返回的data 封装了一个VO:
略
3.、登录接口测试
2、前端开发
实现前端登录器,需要在前端判断用户的登录状态。我们可以像之前那样在组件的 data 中设置一个状态标志,但登录状态应该被视为一个全局属性,而不应该只写在某一组件中。
所以我们需要引入一个新的工具——Vuex,它是专门为 Vue 开发的状态管理方案,我们可以把需要在各个组件中传递使用的变量、方法定义在这里。
略
- 访问前端接口自动跳转到login界面
打开 Application ,在 Session Storage 中看到我们存储的信息-