快速构建一个简单的Springboot-web项目
web项目基本的核心成分
- 数据落地 MYSQL数据库
- 登录标识 JWT :{Java web token }
- 记录有效登录状态 以及缓存常用数据: 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主版本:2.5.1
<?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;
}
}
快速构建一个简单的Springboot-web项目的更多相关文章
- 创建一个简单的 Springboot web项目
1.点击Project 2.点击 Next 3.项目名 4.web 项目 4.确认 5.pom.xml <?xml version="1.0" encoding=" ...
- 构建一个简单的Spring Boot项目
11 构建一个简单的Spring Boot项目 这个章节描述如何通过Spring Boot构建一个"Hello Word"web应用,侧重介绍Spring Boot的一些重要功能. ...
- 快速构建一个简单的单页vue应用
技术栈 vue-cli webpack vux,vux-loader less,less-loader vue-jsonp vue-scroller ES6 vue-cli:一个vue脚手架工具,利用 ...
- 从零构建一个简单的 Python Web框架
为什么你想要自己构建一个 web 框架呢?我想,原因有以下几点: 你有一个新奇的想法,觉得将会取代其他的框架 你想要获得一些名气 你遇到的问题很独特,以至于现有的框架不太合适 你对 web 框架是如何 ...
- 用Java构建一个简单的WebSocket聊天项目之新增HTTP接口调度
采用框架 我们整个Demo基本不需要大家花费太多时间,就可以实现以下的功能. 用户token登录校验 自我聊天 点对点聊天 群聊 获取在线用户数与用户标签列表 发送系统通知 首先,我们需要介绍一下我们 ...
- 一个简单的Java Web项目搭建流程
今天试图在服务器上搭建一个web服务器,顺便回顾了java web项目的入门,使用Servlet处理HTTP请求,并记录日志等操作.当很久没有做过web项目时,有些东西还是很容易忘记的. Maven配 ...
- 一个简单的网站web项目的详解
有不对的术语,或者不好理解的部分,欢迎大家批评指正,谢谢大家! 近期做的网站web项目,实现登录功能,查询功能.首先把这个项目分为几个模块来处理,当前用户模块,历史用户模块,历史记录模块,数据库模块, ...
- IDEA快速创建一个简单的SpringBoot项目(需要联网)
一.点击File-New-Project,选择Spring initializr ,选择jdk1.8及以上 二.填写相关信息,点击Next 3.选择Web -Spring Web,点击Next 4.输 ...
- 用 PyQt5 快速构建一个简单的 GUI 应用
1. 介绍 Python GUI 常用的 3 种框架是:Tkinter.wxpython.PyQt5 PyQt5 基于 Qt,是 Python 和 Qt 的结合体,可以用 Python 语言编写跨平台 ...
随机推荐
- G&GH01 注册/安装/设置
注意事项与声明 平台: Windows 10 作者: JamesNULLiu 邮箱: jamesnulliu@outlook.com 博客: https://www.cnblogs.com/james ...
- AOP实现系统告警
工作群里的消息怕过于安静,又怕过于频繁 一.业务背景 在开发的过程中会遇到各种各样的开发问题,服务器宕机.网络抖动.代码本身的bug等等.针对代码的bug,我们可以提前预支,通过发送告警信息来警示我们 ...
- Django ORM 事务和查询优化
一.事务操作 模块 from django.db import transaction 1 开启事务:with transaction.atomic() from django.db import t ...
- secureCRT登录ubuntu 报错:`No compatible key-exchange method. The server supports these methods: diffie-hellman`
在VMware虚拟机中安装好ubuntu 20.04,已安装并启动sshd,但是使用secureCRT远程登录时则报错: Key exchange failed. No compatible key- ...
- 11_Swagger
一. 引言 Swagger 是一个规范和完整的框架,用于生成.描述.调用和可视化 RESTful 风格的 Web 服务. 总体目标是使客户端和文件系统作为服务器以同样的速度来更新.文件的方法.参数和模 ...
- C++运算符重载(简单易懂)
转载:https://www.cnblogs.com/liuchenxu123/p/12538623.html 运算符重载,就是对已有的运算符重新进行定义,赋予其另一种功能,以适应不同的数据类型. 你 ...
- MySQL安装卸载、idea中Database的使用、常用的sql语句
MySQL安装卸载 MySQL安装 在下面的资源链接中下载MySQL软件压缩包(绿色版),这个版本是MySQL5.7.29的,本教程也只适用于这个绿色版的,如果下载的是安装包那就可能有些地方不一样了, ...
- 开源WindivertDotnet
0 前言 Hi,好久没有写博客,因为近段时间没有新的开源项目给大家.现在终于又写了一篇,是关于网络方向的内容,希望对部分读者有帮助. 1 WinDivert介绍 WinDivert是windows下为 ...
- Kubeadm部署高可用K8S集群
一 基础环境 1.1 资源 节点名称 ip地址 VIP 192.168.12.150 master01 192.168.12.48 master02 192.168.12.242 master03 1 ...
- Tomcat 调优之从 Linux 内核源码层面看 Tcp backlog
前两天看到一群里在讨论 Tomcat 参数调优,看到不止一个人说通过 accept-count 来配置线程池大小,我笑了笑,看来其实很多人并不太了解我们用的最多的 WebServer Tomcat,这 ...