快速构建一个简单的Springboot-web项目
web项目基本的核心成分
- 数据落地 MYSQL数据库
- 登录标识 JWT :
- 记录有效登录状态 以及缓存常用数据: Redis
- 数据库与JAVA实体的快速自动映射ORM:mybatis
- 数据库连接池化技术:Druid
- 视图解析器模板引擎:Thymeleaf
- 基于接口测试和接口文档工具:Swagger
- 单元测试:Junit
- 实体类简化工具:lombok
- SpringbootTest:
项目码云地址:https://gitee.com/gtnotgod/springboot-demo-idea.git
demo示例数据库的表 结构 自己按需更改
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(20) NOT NULL,
`gender` varchar(5) DEFAULT NULL,
`age` int(11) DEFAULT NULL,
`address` varchar(32) DEFAULT NULL,
`qq` varchar(20) DEFAULT NULL,
`email` varchar(50) DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC;
POM文件如下:
SpringBoot主版本:
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.1</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.gton</groupId>
<artifactId>hander</artifactId>
<version>1.0-SNAPSHOT</version>
<!-- JDK-版本:编码设置-->
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--Mybatis ORM-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.0</version>
</dependency>
<!--德鲁伊数据库连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.6</version>
</dependency>
<!--mysql 驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<!-- thymeleaf 模板引擎-视图解析器 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!--单元测试-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!--Springboot Test-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<!--lombok-->
<!-- lombok-实体类简化依赖-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
</dependency>
<!-- swagger2 接口API文档 接口测试 -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
<!--JWT登录认证-->
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.11.0</version>
</dependency>
<!-- redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
</dependencies>
<build>
<!--xml-mapper 资源过滤-->
<resources>
<resource>
<directory>src/main/resource</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
</project>
YML核心配置文件内容如下
server:
port: 8888
spring:
application:
name: springboot-app
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/study?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC&useSSL=false
username: root
password: root
druid:
test-while-idle: false
#Springboot 整合redis数据库
redis:
host: 127.0.0.1
port: 6379
password: 123456
jedis:
pool:
max-active: 8
max-wait: -1
max-idle: 500
min-idle: 0
lettuce:
shutdown-timeout: 0
thymeleaf:
cache: false #关闭缓存,即使刷新 默认 true,关闭之后可以及时刷新页面
mode: HTML5 #默认 HTML5
encoding: UTF-8 # 默认 UTF-8
prefix: classpath:/templates/ #默认 classpath:/templates/
suffix: .html # 默认 .html
mvc:
static-path-pattern: classpath:/static/**
mybatis:
config-location: classpath:maybati-config.xml
mapper-locations: classpath:mapper/*.xml
YML外部引用了Mybatis-config配置文件和指定了Mapper扫描路径;
Mybatis-config:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--开启二级缓存-->
<settings>
<!--开启二级缓存-->
<setting name="cacheEnabled" value="true"/>
<!--配置日志输出-->
<setting name="logImpl" value="STDOUT_LOGGING" />
<!--开启驼峰字段自动转化-->
<setting name="mapUnderscoreToCamelCase" value="true" />
</settings>
<!--配置别名-->
<typeAliases>
<package name="com.entity"/>
</typeAliases>
</configuration>
启动器
@SpringBootApplication
@EnableTransactionManagement //1.开始事物声明式注解处理
public class ApplicationRun {
public static void main(String[] args) {
SpringApplication.run(ApplicationRun.class, args);
}
}
项目目录结构
使用SWagger,需要配置
@Configuration
@EnableSwagger2
public class SwaggerConfig {
@Bean
public Docket createRestApi() {
return new Docket(DocumentationType.SWAGGER_2)
.pathMapping("/")
.select()
.apis(RequestHandlerSelectors.basePackage("com.controller"))
.paths(PathSelectors.any())
.build().apiInfo(new ApiInfoBuilder()
.title("SpringBoot整合Swagger接口测试")
.description("SpringBoot整合Swagger,详细信息......")
.version("1.0")
//new Contact("昵称", "网址链接", "邮箱"))
.contact(new Contact("隔壁老郭", "https://www.cnblogs.com/gtnotgod/", "1054769749@qq.com"))
.license("The Apache License")
.licenseUrl("http://www.baidu.com")
.build());
}
}
Swagger配置还没完,还要开启静态资源过滤
@Configuration
public class webMvcConfig implements WebMvcConfigurer {
/**
* Description: 静态资源过滤
*/
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
//ClassPath:/Static/** 静态资源释放
registry.addResourceHandler("/**").addResourceLocations("classpath:/static/");
//释放swagger
registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");
//释放webjars
registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
}
}
Controller:
@RestController
@Slf4j
@Api(value = "SpringBoot-CRUD-Demo", tags = "基于SpringBoot的增删改查示例Controller")
public class UserController {
@Autowired
private UserService userService;
/**
* Description: http://localhost:8888/getList
*/
@GetMapping(value = "/getList", name = "全部获取数据")
@ApiOperation("全部获取数据-接口")
public RespObject getListByNotLimit() {
List<UserTable> users = userService.getUsers();
return RespObject.respOk(users);
}
/**
* Description: http://localhost:8888/getListById?id=1001
*/
@GetMapping(value = "/getListById", name = "根据ID获取数据")
@ApiOperation("根据ID获取数据-接口")
public RespObject getUserTable(@RequestParam("id") int id) {
UserTable user = userService.getUserById(id);
if (user == null) {
return RespObject.respNo("输入的ID无效");
}
return RespObject.respOk(user);
}
/**
* Description: http://localhost:8888/getUsersByLimit?currentPage=1&pageSize=10
* select * from table limit (start-1)*pageSize,pageSize;
* mysql 分页第一个参数是index{0-max},第二个参数是取的数量
*/
@GetMapping(value = "/getUsersByLimit", name = "分页查询")
@ApiOperation("分页查询-接口")
public RespObject getUserTableByLimit(@RequestParam("currentPage") int currentPage, @RequestParam("pageSize") int pageSize) {
log.info("每一页分:" + pageSize + "条" + ";请求的第:" + currentPage + "页");
if (currentPage == 0) {
return RespObject.respNo("首页坐标是从1开始");
}
List<UserTable> limitUsers = userService.getUserByLimit((currentPage - 1) * pageSize, pageSize);
return RespObject.respOk(limitUsers);
}
/**
* Description: 添加操作
*/
@PostMapping(value = "/addUserTable", name = "添加表数据")
@ApiOperation("添加表数据-接口")
public RespObject addUserTableData(@RequestBody UserTable userTable) {
System.out.println(userTable);
int id = userTable.getId();
//判断ID是否存在-{isPresent存在就返回true}
if (id != 0) {
return RespObject.respNo("添加操作不允许传递主键");
}
int rowChange = 0;
try {
rowChange = userService.insertInToUserTable(userTable);
} catch (Exception e) {
e.printStackTrace();
}
return rowChange > 0 ? RespObject.respOk() : RespObject.respNo("添加失败");
}
/**
* Description: 删除操作
*/
@DeleteMapping(value = "/delUserTableById/{id}", name = "删除表数据")
@ApiOperation("删除表数据-接口")
public RespObject addUserTableData(@PathVariable("id") int id) {
int rowChange = 0;
try {
rowChange = userService.deleteusertablebyid(id);
} catch (Exception e) {
e.printStackTrace();
}
return rowChange > 0 ? RespObject.respOk() : RespObject.respNo("该ID无效-删除失败");
}
/**
* Description: 修改操作
*/
@PutMapping(value = "/updateUserTableById", name = "修改表数据")
@ApiOperation("修改表数据-接口")
public RespObject updateUserTableData(@RequestBody UserTable userTable) {
int id = userTable.getId();
//判断-{isPresent存在就返回true}
if (id == 0) {
return RespObject.respNo("修改操作必须传递主键");
}
int rowChange = 0;
try {
rowChange = userService.updateUserTableById(userTable);
} catch (Exception e) {
e.printStackTrace();
}
return rowChange > 0 ? RespObject.respOk() : RespObject.respNo("修改失败");
}
}
Service
public interface UserService {
/**
* Description: 无条件查询
*/
List<UserTable> getUsers();
/**
* Description:条件查询
*/
UserTable getUserById(int userId);
/**
* Description:分页查询
*/
List<UserTable> getUserByLimit(int startIndex, int everyPageSize);
/**
* Description: 新增 返回的结果是影响行数
*/
int insertInToUserTable(UserTable tableObj);
/**
* Description: 修改
*/
int updateUserTableById(UserTable tableObj);
/**
* Description:删除
*/
int deleteusertablebyid(int id);
/**
* Description:登录
*/
UserTable queryForEntity(LoginUser user);
/**
* Description: 根据Email查询
*/
UserTable getbyUserEmail(String userEmail);
}
ServiceImpl
@Service
public class UserServiceImpl implements UserService {
@Resource
private UserMapper userMapper;
@Override
public List<UserTable> getUsers() {
return userMapper.getUsers();
}
@Override
public UserTable getUserById(int userId) {
return userMapper.getUserById(userId);
}
@Override
public List<UserTable> getUserByLimit(int startIndex, int everyPageSize) {
return userMapper.getUserByLimit(startIndex, everyPageSize);
}
@Override
public int insertInToUserTable(UserTable tableObj) {
return userMapper.insertInToUserTable(tableObj);
}
@Override
public int updateUserTableById(UserTable tableObj) {
return userMapper.updateUserTableById(tableObj);
}
@Override
public int deleteusertablebyid(int id) {
return userMapper.deleteusertablebyid(id);
}
@Override
public UserTable queryForEntity(LoginUser user) {
String username = user.getUsername();
String password = user.getPassword();
return userMapper.selectByUserNameAndPassword(username, password);
}
@Override
public UserTable getbyUserEmail(String userEmail) {
return userMapper.selectByEmail(userEmail);
}
}
Mapper Interface
@Mapper
public interface UserMapper {
/**
* Description: 无条件查询
*/
List<UserTable> getUsers();
/**
* Description:条件查询
*/
UserTable getUserById(@Param("userId") int userId);
/**
* Description:分页查询
*/
List<UserTable> getUserByLimit(@Param("startIndex") int startIndex, @Param("everyPageSize") int everyPageSize);
/**
* Description: 新增 返回的结果是影响行数
*/
int insertInToUserTable(UserTable tableObj);
/**
* Description: 修改
*/
int updateUserTableById(UserTable tableObj);
/**
* Description:删除
*/
int deleteusertablebyid(@Param("id") int id);
UserTable selectByUserNameAndPassword(@Param("username") String username, @Param("password") String password);
UserTable selectByEmail(@Param("userEmail") String userEmail);
}
Mapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.mapper.UserMapper">
<sql id="tableFields">
id,name ,gender,age,address,qq,email
</sql>
<insert id="insertInToUserTable" parameterType="userTable" useGeneratedKeys="true" keyProperty="id">
insert into user (name ,gender,age,address,qq,email) values (#{name},#{gender},#{age},#{address},#{qq},#{email})
</insert>
<update id="updateUserTableById">
update user
<set>
<if test="name!=null">
name=#{name},
</if>
<if test="gender!=null">
gender=#{gender},
</if>
<if test="age!=null">
age=#{age},
</if>
<if test="address!=null">
address=#{address},
</if>
<if test="qq!=null">
qq=#{qq},
</if>
<if test="email!=null">
email=#{email},
</if>
</set>
where id=#{id}
</update>
<delete id="deleteusertablebyid">
delete from user where id=#{id}
</delete>
<!--全部查询-->
<select id="getUsers" resultType="userTable">
select
<include refid="tableFields"/>
from user;
</select>
<!--条件查询-->
<select id="getUserById" resultType="userTable">
select * from user where id=#{userId}
</select>
<!--分页查询-->
<select id="getUserByLimit" resultType="com.entity.UserTable">
select * from user limit #{startIndex},#{everyPageSize}
</select>
<select id="selectByUserNameAndPassword" resultType="com.entity.UserTable">
select * from user where name=#{username} and address =#{password}
</select>
<select id="selectByEmail" resultType="com.entity.UserTable">
select * from user where email =#{userEmail}
</select>
</mapper>
整合JWT
自定义需要登录注解:不需要登录注解
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface NeedTokenByJWT {
boolean required() default true;
}
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface SkipTokenByJWT {
boolean required() default true;
}
JWT拦截器配合Redis记录状态,控制登录访问
public class AuthenticationInterceptor implements HandlerInterceptor {
/**
* Description: HandlerInterceptor接口主要定义了三个方法
* 1.boolean preHandle ():
* 预处理回调方法,实现处理器的预处理,第三个参数为响应的处理器,自定义Controller,返回值为true表示继续流程(如调用下一个拦截器或处理器)或者接着执行
* postHandle()和afterCompletion();false表示流程中断,不会继续调用其他的拦截器或处理器,中断执行。
* <p>
* 2.void postHandle():
* 后处理回调方法,实现处理器的后处理(DispatcherServlet进行视图返回渲染之前进行调用),此时我们可以通过modelAndView(模型和视图对象)对模型数据进行处理或对视图进行处理,modelAndView也可能为null。
* <p>
* 3.void afterCompletion():
* 整个请求处理完毕回调方法,该方法也是需要当前对应的Interceptor的preHandle()的返回值为true时才会执行,也就是在DispatcherServlet渲染了对应的视图之后执行。用于进行资源清理。整个请求处理完毕回调方法。如性能监控中我们可以在此记录结束时间并输出消耗时间,还可以进行一些资源清理,类似于try-catch-finally中的finally,但仅调用处理器执行链中
*
* @author: GuoTong
* @date: 2021-06-28 15:26:49
* @param:
* @return:
*/
@Autowired
UserService userService;
@Autowired
private RedisTemplate redisTemplate;
/**
* Description: 主要流程:
* <p>
* 1.从 http 请求头中取出 token,
* 2.判断是否映射到方法
* 3.检查是否有SkipTokenByJWT注解注释,有则跳过认证
* 4.检查有没有需要用户登录的注解NeedTokenByJWT,有则需要取出并验证
* 5.认证通过则可以访问,不通过会报相关错误信息
*
* @author: GuoTong
* @date: 2021-06-28 15:27:55
* @param:
* @return:
*/
@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object object) throws Exception {
// 从 http 请求头中取出 token
String token = httpServletRequest.getHeader("token");
// 如果不是映射到方法直接通过
if (!(object instanceof HandlerMethod)) {
return true;
}
HandlerMethod handlerMethod = (HandlerMethod) object;
Method method = handlerMethod.getMethod();
//检查是否有SkipTokenByJWT注释,有则跳过认证
if (method.isAnnotationPresent(SkipTokenByJWT.class)) {
SkipTokenByJWT SkipTokenByJWT = method.getAnnotation(SkipTokenByJWT.class);
if (SkipTokenByJWT.required()) {
return true;
}
}
//检查有没有需要用户权限的注解
if (method.isAnnotationPresent(NeedTokenByJWT.class)) {
NeedTokenByJWT NeedTokenByJWT = method.getAnnotation(NeedTokenByJWT.class);
if (NeedTokenByJWT.required()) {
// 执行认证
if (token == null) {
throw new RuntimeException("无token,请重新登录");
}
// 获取 token 中的 user id
String userEmail;
try {
userEmail = JWT.decode(token).getAudience().get(0);
} catch (JWTDecodeException j) {
throw new MyLoginException("401,请重新登录");
}
UserTable user = userService.getbyUserEmail(userEmail);
if (user == null) {
throw new RuntimeException("用户不存在,请重新登录");
}
// 验证 token
JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(user.getAddress())).build();
try {
jwtVerifier.verify(token);
//Redis如果存在就判断token是否一致
String redisToken = (String) redisTemplate.opsForValue().get(user.getEmail());
if (!StringUtils.equals(token, redisToken)) {
throw new RuntimeException("用户登录状态已过期");
}
} catch (JWTVerificationException e) {
throw new RuntimeException("未检测到用户登录401");
}
return true;
}
}
return true;
}
@Override
public void postHandle(HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse,
Object o, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse,
Object o, Exception e) throws Exception {
}
}
注册拦截器 、编写Redis序列化配置
@Configuration
public class webMvcConfig implements WebMvcConfigurer {
/**
* Description: 静态资源过滤
*/
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
//ClassPath:/Static/** 静态资源释放
registry.addResourceHandler("/**").addResourceLocations("classpath:/static/");
//释放swagger
registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");
//释放webjars
registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
}
/**
* Description:添加基于JWT认证的拦截器
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 拦截所有请求,通过判断是否有 @SkipTokenByJWT 注解 决定是否需要登录
registry.addInterceptor(getInterceptorByJwt()).addPathPatterns("/**");
}
@Bean
public AuthenticationInterceptor getInterceptorByJwt() {
return new AuthenticationInterceptor();
}
/*解决RedisTemplate往redis存入的数据是二进制文件(不管是key还是value都是二进制文件),自定义json序列化与反序列化规则*/
/**
* redisTemplate 序列化使用的jdkSerializeable, 存储二进制字节码, 所以自定义序列化类
*
* @param redisConnectionFactory
* @return
*/
@Bean
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(redisConnectionFactory);
// 使用Jackson2JsonRedisSerialize 替换默认序列化
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
// 设置value的序列化规则和 key的序列化规则
redisTemplate.setKeySerializer(new StringRedisSerializer());
//jackson2JsonRedisSerializer就是JSON序列号规则,
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
@Bean
@ConditionalOnMissingBean(StringRedisTemplate.class)
public StringRedisTemplate stringRedisTemplate(
RedisConnectionFactory redisConnectionFactory) {
StringRedisTemplate template = new StringRedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
}
自定义异常
public class MyLoginException extends RuntimeException {
public MyLoginException(String message) {
super(message);
}
}
全局异常处理
@RestControllerAdvice
public class GlobalExceptionHander {
private ObjectMapper objectMapper = new ObjectMapper();
private final int NO_LOGIN_CODE = 401;
@ExceptionHandler(value = Exception.class)
public RespObject exceptionHandler(Exception e) {
e.printStackTrace();
return RespObject.respNo(e.getMessage());
}
@ExceptionHandler(value = MyLoginException.class)
public RespObject MyLoginException(Exception e) {
e.printStackTrace();
String message = e.getMessage();
if (String.valueOf(NO_LOGIN_CODE).equals(message)) {
return RespObject.respOk(NO_LOGIN_CODE, e.getMessage());
}
return RespObject.respNo(e.getMessage());
}
}
编写整合JWTcontroller
@RestController
public class HelloController {
@Autowired
private UserService userService;
@Autowired
private RedisTemplate redisTemplate;
private final static Map<String, Object> RESP;
static {
RESP = new HashMap<>();
RESP.put("author", "郭童");
RESP.put("since", "JDK1.8");
RESP.put("createTime", "2021-06-28 09:28");
RESP.put("backFrame", "SpringBoot2.5.1");
RESP.put("htmlTemp", "Thymeleaf");
}
@RequestMapping(value = "/hello", name = "web项目测试")
public String gotoHelloWorld() {
return "hello world!";
}
@RequestMapping(value = "/", name = "设置默认访问页面")
public ModelAndView gotoIndexPage() {
ModelAndView view = new ModelAndView("index");
view.addObject("initData", RESP);
return view;
}
@SkipTokenByJWT
@PostMapping(value = "/login", name = "登录")
public Object loginUser(@RequestBody LoginUser user) {
UserTable userTable = userService.queryForEntity(user);
if (userTable == null) {
return RespObject.respNo("用户名或者密码错误");
}
String token = user.getToken(userTable);
//缓存登录状态
String emailIsRedisKeyByLife = userTable.getEmail();
String dataRedis = (String) redisTemplate.opsForValue().get(emailIsRedisKeyByLife);
if (StringUtils.isEmpty(dataRedis)) {
redisTemplate.opsForValue().set(emailIsRedisKeyByLife, token);
//设置过期时间; TimeUnit.MILLISECONDS 毫秒:设置默认时间是SECONDS秒:60秒
redisTemplate.expire(emailIsRedisKeyByLife, 60, TimeUnit.MINUTES);
}
return RespObject.respLogin(token, userTable);
}
@GetMapping(value = "/getUserById", name = "根据ID获取数据")
@NeedTokenByJWT
public RespObject getUserTable(@RequestParam("id") int id) {
UserTable user = userService.getUserById(id);
if (user == null) {
return RespObject.respNo("输入的ID无效");
}
return RespObject.respOk(user);
}
}
整合HTML首页
<!DOCTYPE html>
<!--suppress ALL-->
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head th:include="/common/common.html">
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>首页</title>
</head>
<body>
<h1 class="text-center">Hello</h1>
<table class="table table-hover indexTable" th:object="${initData}">
<tr class="active">
<td>项目编码作者</td>
<td th:text="*{author}"></td>
</tr>
<tr class="success">
<td>使用JAVA版本</td>
<td th:text="*{since}"></td>
</tr>
<tr class="warning">
<td>项目创建时间</td>
<td th:text="*{createTime}"></td>
</tr>
<tr class="danger">
<td>后端使用框架</td>
<td th:text="*{backFrame}"></td>
</tr>
<tr class="info">
<td>视图模板引擎</td>
<td th:text="*{htmlTemp}"></td>
</tr>
</table>
<form class="indexTable" id="formByThisPage" onsubmit="return false">
<div class="form-group">
<label for="exampleInputEmail1">ID:</label>
<input type="text" class="form-control" id="userTableId"
placeholder="请输入ID">
</div>
<button id="btnByThisA" type="button" class="btn btn-default">查询</button>
</form>
<table class="table table-hover indexTable">
<tr>
<td>查询结果</td>
<td>
<textarea class="form-control" rows="3" id="selectText"></textarea>
</td>
</tr>
</table>
<form class="indexTable" id="formByThisPage" th:action="@{/login}" method="post" onsubmit="return false">
<div class="form-group">
<label for="exampleInputEmail1">用户名:</label>
<input type="text" name="username" class="form-control" id="exampleInputEmail1"
placeholder="请输入用户名">
</div>
<div class="form-group">
<label for="exampleInputPassword1">密码:</label>
<input type="password" name="password" class="form-control" id="exampleInputPassword1" placeholder="请输入密码">
</div>
<button id="btnByThis" type="button" class="btn btn-default">登录</button>
</form>
<script type="text/javascript">
$(function (ev) {
$("#btnByThis").on('click', function () {
window.localStorage.token = undefined;
let sendLoginData = {username: $("#exampleInputEmail1").val(), password: $("#exampleInputPassword1").val()}
$.ajax({
url: "/login",
type: "post",
data: JSON.stringify(sendLoginData),
dataType: "json",
contentType: "application/json; charset=utf-8",
success: function (resp) {
if (resp.code == 200) {
toastr.success("登录成功!");
window.localStorage.token = resp.token;
} else {
toastr.error(resp.msg);
}
}
});
})
$("#btnByThisA").on('click', function () {
let sendLoginData = $("#userTableId").val();
$.ajax({
url: "/getUserById?id=" + sendLoginData,
type: "get",
beforeSend: function (XMLHttpRequest) {
XMLHttpRequest.setRequestHeader("token", window.localStorage.token);
},
success: function (resp) {
if (resp.code == 200) {
$("#selectText").val(JSON.stringify(resp.data));
toastr.success("查询成功!!");
} else if (resp.code) {
$("#selectText").val(JSON.stringify(resp.data));
toastr.error(resp.msg);
}
}
});
})
});
</script>
</body>
</html>
实体类
@Data
public class UserTable {
private int id;
private String name;
private String address;
private String gender;
private String qq;
private String email;
private int age;
}
@Data
@Accessors(chain = true)
public class LoginUser {
String Id;
String username;
String password;
/**
* Description: Algorithm.HMAC256():使用HS256生成token,密钥则是用户的密码,唯一密钥的话可以保存在服务端。
* withAudience()存入需要保存在token的信息,这里我把用户getEmail存入token中
* @author: GuoTong
* @date: 2021-06-28 15:19:59
* @param:
* @return:
*/
public String getToken(UserTable user) {
String token = "";
token = JWT.create().withAudience(user.getEmail())
.sign(Algorithm.HMAC256(user.getAddress()));
return token;
}
}
作者:隔壁老郭
个性签名:独学而无友,则孤陋而寡闻。做一个灵魂有趣的人!
如果觉得这篇文章对你有小小的帮助的话,记得在右下角点个“推荐”哦,博主在此感谢!
Java入门到入坟
万水千山总是情,打赏一分行不行,所以如果你心情还比较高兴,也是可以扫码打赏博主,哈哈哈(っ•̀ω•́)っ✎⁾⁾!