后端开发----Spring Boot +MyBatis(MySQL)
一、项目的搭建
使用IDEA编辑器创建好项目,具体项目结构如图
- 在pom.xml(Maven管理器),添加好对应依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<!--继承Spring Boot的父级项目的依赖-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>springboot_vue</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springboot_vue</name>
<description>Demo project for Spring Boot</description>
<!--打war包-->
<packaging>war</packaging>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!--springboot开发web项目的起步依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--测试的起步依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--springboot开发自动热部署-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
<!--JSON依赖包-->
<dependency>
<groupId>net.sf.json-lib</groupId>
<artifactId>json-lib</artifactId>
<version>2.2.3</version>
<classifier>jdk15</classifier>
</dependency>
<!--加载Mybatis整合springboot-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.1</version>
</dependency>
<!--Mysql的jdbc驱动包-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.35</version>
</dependency>
<!--java-jjwt依赖-->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<!--springboot项目编译打包插件-->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<!--mybatis代码自动生成插件-->
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<version>1.3.6</version>
<configuration>
<!--配置文件的位置-->
<configurationFile>GeneratorMapper.xml</configurationFile>
<verbose>true</verbose>
<overwrite>true</overwrite>
</configuration>
</plugin>
</plugins>
<!--编译文件所在目录、类型,到指定位置-->
<resources>
<!--编译XML文件到class目录-->
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
<!--所有配置文件编译到class目录-->
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.*</include>
</includes>
</resource>
</resources>
</build>
</project>
- GeneratorMapper.xml,添加数据库生成的信息,使用Mybatis代码自动生成插件,生成数据库中对应的model类和mapper类
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN" "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd" >
<generatorConfiguration>
<!--需要修改,jar包位置-->
<!-- 指定连接数据库的JDBC驱动jar包所在位置,指定到本机的完整路径 -->
<classPathEntry location="D:\Code\Java\jar\mysql-connector-java-5.1.13-bin.jar" />
<!--配置tbale表信息内容体,targetRuntime指定采用MyBatis3的版本,一个数据库一个context-->
<context id="tables" targetRuntime="MyBatis3">
<!--抑制生成注释,由于生成的注释都是英文的,可以不生成-->
<commentGenerator>
<property name="suppressAllComments" value="true"></property>
</commentGenerator>
<!--需要修改,账户密码-->
<!--配置数据库连接信息,指定数据库名-->
<jdbcConnection driverClass="com.mysql.jdbc.Driver" connectionURL="jdbc:mysql://127.0.0.1:3306/dbtest"
userId="root" password="admin">
</jdbcConnection>
<!--需要修改,下列相关包名-->
<!--生成model类,targetPackage指定model类的包名,targetProject指定生成的model放在IDEA的哪个工程下面-->
<javaModelGenerator targetPackage="com.example.springboot_vue.model" targetProject="src/main/java">
<!-- 是否在当前路径下新加一层schema,eg:false路径com.example.springboot.model, true:com.example.springboot.model.[schemaName] -->
<property name="enableSubPackages" value="false"></property>
<!-- 是否针对string类型的字段在set的时候进行trim调用 -->
<property name="trimStrings" value="false"></property>
</javaModelGenerator>
<!--需要修改,下列相关包名-->
<!--生成MyBatis的Mapper.xml文件,targetPackage指定mapper.xml文件的包名,targetProject指定生成的mapper.xml放在什么位置-->
<sqlMapGenerator targetPackage="com.example.springboot_vue.mapper" targetProject="src/main/java">
<property name="enableSubPackages" value="false"></property>
</sqlMapGenerator>
<!--需要修改,下列相关包名-->
<!--生成MyBatis的Mapper接口类文件,targetPackage指定Mapper接口类的包名,targetProject指定生成的Mapper接口放在eclipse的哪个工程下面-->
<javaClientGenerator type="XMLMAPPER" targetPackage="com.example.springboot_vue.mapper" targetProject="src/main/java">
<property name="enableSubPackages" value="false"></property>
</javaClientGenerator>
<!--需要修改,表名-->
<!--数据库表名及对应生成的java模型名-->
<!-- schema即为数据库名 tableName为对应的数据库表 domainObjectName是要生成的实体类
enable*ByExample 是否生成 example类 -->
<table tableName="userinfo"
domainObjectName="UserInfo" enableCountByExample="false"
enableDeleteByExample="false" enableSelectByExample="false"
enableUpdateByExample="false" selectByExampleQueryId="false">
</table>
</context>
</generatorConfiguration>
- application.properties,项目属性配置
#项目描述
info.project-url=http://127.0.0.1:8089/springboot_vue/
info.author=lingfengzi
info.version=1.0.0
#设置项目路径
server.servlet.context-path=/springboot_vue
#设置项目访问端口号
server.port=8089
#配置数据库连接信息
spring.datasource.username=root
spring.datasource.password=admin
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/dbtest?useUnicode=true&characterEncoding=utf-8&useSSL=false
二、代码编辑
- 设置java web token生成工具,utils/JwtUtil.java。
package com.example.springboot_vue.utils;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import net.sf.json.JSONObject;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
import java.security.Key;
import java.util.Date;
public class JwtUtil {
private static final long EXPIRATION_TIME = (long)(168*3600*1000); // 有效时间:7天 24h*7=168h,转换为时间戳单位
private static final String SECRET = "secret";// 密钥
//解析
public static JSONObject parseJWT(String jsonWebToken) {
try{
Claims claims = Jwts.parser()
.setSigningKey(DatatypeConverter.parseBase64Binary(SECRET))
.parseClaimsJws(jsonWebToken).getBody();
//System.out.println(claims);
JSONObject json=new JSONObject();
//json.put("userId",claims.get("userId"));
//json.put("password",claims.get("password"));
json.put("nbf",claims.getNotBefore().getTime());
json.put("exp",claims.getExpiration().getTime());
return json;
}catch (Exception e){
return null;
}
}
//生成
public static String createJWT(String userId, String password) {
SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
//当前时间
long nowMillis = System.currentTimeMillis();
Date now = new Date(nowMillis);
//生成签名密钥
byte[] apiKeySecretBytes = DatatypeConverter.parseBase64Binary(SECRET);
Key signingKey = new SecretKeySpec(apiKeySecretBytes, signatureAlgorithm.getJcaName());
//添加构成JWT的参数
JwtBuilder jwtBuilder = Jwts.builder().setHeaderParam("type", "JWT")
.claim("userId", userId)
.claim("password", password)
.signWith(signatureAlgorithm, signingKey);
//添加Token过期时间
long expMillis = nowMillis + EXPIRATION_TIME;
Date exp = new Date(expMillis);
jwtBuilder.setExpiration(exp).setNotBefore(now);
//返回生成的JWT字符串
return jwtBuilder.compact();
}
public static void main(String[] args){
String token = JwtUtil.createJWT("10001", "123456");
System.out.println(token);
JSONObject json=JwtUtil.parseJWT(token);
System.out.println(json.toString());
}
}
- 在service包下设置数据库服务功能接口以及实现,然后在mapper下写入调用数据库的语句;
service/impl/UserInfoServiceImpl .java
package com.example.springboot_vue.service.impl;
import com.example.springboot_vue.mapper.UserInfoMapper;
import com.example.springboot_vue.model.UserInfo;
import com.example.springboot_vue.service.UserInfoService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserInfoServiceImpl implements UserInfoService {
@Autowired
UserInfoMapper userInfoMapper;
@Override
public UserInfo loginCheck(String username, String password) {
return userInfoMapper.loginCheck(username,password);
}
@Override
public int updateUserToken(String username, String token) {
return userInfoMapper.updateUserToken(username,token);
}
@Override
public UserInfo selectByToken(String token) {
return userInfoMapper.selectByToken(token);
}
}
mapper/UserInfoMapper.xml
<!--验证登录,并取得用户信息-->
<select id="loginCheck" parameterType="String" resultMap="BaseResultMap">
select
id,username,password
from userinfo
where username=#{username,jdbcType=VARCHAR}
and password = #{password,jdbcType=VARCHAR}
</select>
<!--更新用户令牌-->
<update id="updateUserToken" parameterType="String">
update userinfo
set
token = #{token,jdbcType=VARCHAR}
where username=#{username,jdbcType=VARCHAR}
</update>
<!--通过令牌换取用户信息-->
<select id="selectByToken" parameterType="String" resultMap="BaseResultMap">
select
id,username,password
from userinfo
where token = #{token,jdbcType=VARCHAR}
</select>
- 登录请求控制器,controller/LoginController.java
package com.example.springboot_vue.controller;
import com.example.springboot_vue.model.UserInfo;
import com.example.springboot_vue.service.UserInfoService;
import com.example.springboot_vue.utils.JwtUtil;
import net.sf.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;
@Controller
public class LoginController {
@Autowired
UserInfoService userInfoService;
@RequestMapping("/loginCheck")
public @ResponseBody JSONObject loginCheck(@RequestBody JSONObject data, HttpServletResponse response){
JSONObject json=new JSONObject();
String username=data.getString("username");
String password=data.getString("password");
UserInfo userInfo= userInfoService.loginCheck(username,password);
if(userInfo!= null){
json.put("code",0);
json.put("data",userInfo);
json.put("msg","");
String token= JwtUtil.createJWT(userInfo.getId().toString(),password);
//更新存储令牌,一般存于redis中,示例存储在mysql
userInfoService.updateUserToken(username,token);
//从http头部设置token
response.setHeader("authorization",token);
// axios请求,跨域的情况下,这样设置才能从headers中拿到token
response.setHeader("Access-Control-Expose-Headers", "authorization");
}else{
json.put("code",500);
json.put("data",null);
json.put("msg","用户名或密码错误");
}
return json;
}
@RequestMapping("/getUserInfo")
public @ResponseBody JSONObject getUserInfo(HttpServletRequest request){
String token=request.getHeader("authorization");
JSONObject json=JwtUtil.parseJWT(token);
long exp=Long.parseLong(json.getString("exp"));//过期时间
long nbf=new Date().getTime();
if(exp>nbf){
UserInfo userInfo= userInfoService.selectByToken(token);
if(userInfo !=null){
json.put("code",0);
json.put("data",userInfo);
json.put("msg","");
}else{
json.put("code",500);
json.put("data",null);
json.put("msg","登录信息不存在,请重新登录");
}
}else{
json.put("code",501);
json.put("data",null);
json.put("msg","登录信息已失效,请重新登录");
}
return json;
}
}
- 在本地浏览时,前后端开了两个服务器,会存在浏览器跨域问题,所以要设置允许跨域
config/WebConfig.java
package com.example.springboot_vue.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
//设置允许跨域的路径
registry.addMapping("/**")
//设置允许跨域请求的域名
//当**Credentials为true时,**Origin不能为星号,需为具体的ip地址【如果接口不带cookie,ip无需设成具体ip】
.allowedOrigins("http://localhost:8080")
//是否允许证书 不再默认开启
.allowCredentials(true)
//设置允许的方法
.allowedMethods("*")
//跨域允许时间
.maxAge(3600);
}
}
至此,Spring Boot +Vue,前后端分离项目已经基本完成,并且测试通信成功。