参考资料
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 {@AutowiredLoginService 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;@Overridepublic 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 {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws IOException {//从header中获取tokenString token = request.getHeader("token");//如果token为空if (StringUtils.isBlank(token)) {setReturn(response,400,"用户未登录,请先登录");return false;}//在实际使用中还会:// 1、校验token是否能够解密出用户信息来获取访问者// 2、token是否已经过期return true;}@Overridepublic 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*/@Overridepublic void addInterceptors(InterceptorRegistry registry) {//添加拦截器registry.addInterceptor(new LoginInterceptor()).addPathPatterns("/api/**")//放行路径,可以添加多个.excludePathPatterns("/api/login");}}
1.3、跨域配置
在之前的后台接口,有一个注解@CrossOrigin ,这个注解是用来跨域的,每个接口都写一遍肯定是不太方便的,这里我们 创建跨域配置类并添加统一的跨域配置:
@Configuration
public class CorsConfig {@Beanpublic 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;@Overridepublic 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 中看到我们存储的信息-