快速构建一个简单的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 语言编写跨平台 ...
随机推荐
- 纯CSS实现“流星赶月”,祝大家中秋节快乐
明天就是中秋节了,就想着用CSS画一个月亮送给园友们吧.但是就画一个月亮也太简单了些,于是便加了一些星星点缀以及流星坠落的效果.这篇文章就用纯CSS为大家实现一个"流星赶月"的效果 ...
- 从代码到发包,一个程序全搞定!Gitea 推出软件包自托管功能 Package Registry
2022 年 7 月的最后一天,随着 Gitea 1.17.0 版本的正式发布,Gitea 开源社区推出了一项名为 Package Registry 的包管理功能,与 Gitea 代码仓库无缝集成,类 ...
- C/C++内存泄漏检测方法
1. 内存泄漏 内存泄漏(Memory Leak)是指程序中已动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果. 2. 检测代码 使用链 ...
- 使用J2EE 登录实例开发
我们先了解下Servlet的生命周期 Servlet部署在容器里,其生命周期由容器管理. 概括为以下几个阶段: 1)容器加载Servlet类. 当第一次有Web客户请求Servlet服务或当Web服务 ...
- 算法:Manacher,给定一个字符串str,返回str中最长回文子串的长度。
[题目] 给定一个字符串str,返回str中最长回文子串的长度 [举例] str="123", 1 str="abc1234321ab" 7 [暴力破解] 从左 ...
- 使用 Docker 安装 Elastic Stack 8.0 并开始使用
文章转载自:https://mp.weixin.qq.com/s/fLnIzbbqYfILS6uCvGctXw 运行 Elasticsearch docker network create elast ...
- 域名服务DNSmasq搭建
假设该服务端主机ip是:192.168.80.100 服务端安装(yum方式) yum install dnsmasq -y # 配置系统文件 # cp /etc/resolv.conf /etc/r ...
- Springboot之 Mybatis 多数据源实现
简介 上篇讲解了 JPA 多数据源实现:这篇讲解一下 Mybatis 多数据源实现 .主要采用将不同数据库的 Mapper 接口分别存放到不同的 package,Spring 去扫描不同的包,注入不同 ...
- Java线程同步的四种方式详解(建议收藏)
Java线程同步属于Java多线程与并发编程的核心点,需要重点掌握,下面我就来详解Java线程同步的4种主要的实现方式@mikechen 目录 什么是线程同步 线程同步的几种方式 1.使用sync ...
- 洛谷P2627 [USACO11OPEN]Mowing the Lawn G (单调队列优化DP)
一道单调队列优化DP的入门题. f[i]表示到第i头牛时获得的最大效率. 状态转移方程:f[i]=max(f[j-1]-sum[j])+sum[i] ,i-k<=j<=i.j的意义表示断点 ...