快速构建一个简单的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 语言编写跨平台 ...
随机推荐
- Batch Norm 与 Layer Norm 比较
一.结论 Batch Norm一般用于CV领域,而Layer Norm一般用于NLP领域 Batch Norm需要计算全局平均,而Layer Norm不需要计算全局平均 二.Batch Norm Ba ...
- 跟羽夏学 Ghidra ——初识
写在前面 此系列是本人一个字一个字码出来的,包括示例和实验截图.本人非计算机专业,可能对本教程涉及的事物没有了解的足够深入,如有错误,欢迎批评指正. 如有好的建议,欢迎反馈.码字不易,如果本篇文章 ...
- Webpack与Vite热更新差异对比
随着项目的日渐迭代,项目整体的代码量也会越来越多,从而导致项目体积越来越大:在Webpack时代,很多人会对历史项目(巨型项目)感到头疼,因为往往巨型项目在本地开发调试的时候会因为本地代码的修改触发H ...
- Java的线程状态
在我们平时写code的时候,经常会使用到多线程.其中线程所处的状态就是我们需要进程思考的问题. 线程有哪些状态 NEW: 一个线程刚被创建,但是没有被使用就是处于这个状态 RUNNABLE: 一个线程 ...
- Kubernetes 配置管理
ConfigMap(可变配置管理) 对于应用的可变配置在 Kubernetes 中是通过一个 ConfigMap 资源对象来实现的,我们知道许多应用经常会有从配置文件.命令行参数或者环境变量中读取一些 ...
- Portainer 基本功能介紹之升級映像檔並更新 Container
文档地址:https://www.asustor.com/zh-tw/online/College_topic?topic=145#dpt7
- CentOS7内置Realtek网卡驱动r8169降级r8168
前几天装了几台服务器测试,在使用的过程中发现,每次重启系统,登录界面会弹出网卡提示 "r8169 0000:02:00 eth0 Invalid ocp reg 17758!" ...
- jq判断页面滚动条进行样式修改
$(window).scroll(function(){//窗口的滚动条 if($(window).scrollTop()>100){ //垂直滚动条钓offset 大于90时. $(" ...
- # 如何在Windows下运行Linux程序
如何在Windows下运行Linux程序 一.搭建 Linux 环境 1.1 安装 VMware Workstation https://www.aliyundrive.com/s/TvuMyFdTs ...
- ECMAScript6 ES6 ES2015新语法总结
1.let定义变量:不能重复定义.作用域 2.const:定义常量 3.解构赋值:let [a,b,c] = [1,2,3];// a=1 b=2 c=3 4.箭头函数: function fn(a, ...