SpringBoot实战1
一、开发环境,环境搭建-----创建项目
通过传统的Maven工程进行创建SpringBoot项目
(1)导入SpringBoot项目开发所需要的依赖
一个父依赖:(工件ID为:spring-boot-starter-parent
)
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.4.4</version>
</parent>
开发相关依赖
Web依赖:spring-boot-starter-web
<!--引入Web的依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
Mybatis依赖:mybatis-spring-boot-starter
<!--引入Mybatis的依赖-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>3.0.4</version>
</dependency>
MySQL的驱动依赖:mysql-connector-j
<!--引入MySQL的驱动依赖-->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
</dependency>
lombok工具依赖:
<!--lombok依赖-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
(2)创建基本的项目架构
Controller,Service,Mapper,POJO,utils
(3)在resources目录下加入application.yml的SpringBoot项目配置文件,在其中加入Jdbc的相关配置
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/big_event?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC
username: root
password: ****
(4)完成SQL配置,连接数据库,同时创建数据库表
(5)添加SpringBoot的启动类:(项目名)XxxApplication
加上@SpringBootApplication
注解确定为启动类
@SpringBootApplication
public class BigEventApplication
{
public static void main( String[] args) {
SpringApplication.run(BigEventApplication.class, args);
}
}
二、注册功能实现
localhost:8080/user/register
Controller层加上@RestController—>@ResponseBody可以自动将返回值转为JSON格式
通过Post的方式提交数据
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private UserService userService;
@PostMapping("/register")
public Result register(String username, String password){
// TODO: 查询用户是否存在
User user=userService.findByUserName(username);
if (user!=null){
return Result.error("用户已存在");
}else {
// TODO: 用户不存在的话就开始注册用户
userService.register(username,password);
return Result.success();
}
}
}
编写对应的业务层 ,数据层的操作
public interface UserService {
// 注册
void register(String username, String password) ;
// 根据用户名查询用户
User findByUserName(String username);
}
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Override
public void register(String username, String password) {
// TODO: 对于用于的密码进行MD5加密处理
String md5Password = Md5Util.getMD5String(password);
userMapper.add(username,md5Password);
}
@Override
public User findByUserName(String username) {
User user =userMapper.findByUserName(username);
return user;
}
}
@Mapper
public interface UserMapper {
// TODO: 必须加入创建时间,更新时间通过sql自带的now()函数
@Insert("insert into user(username,password,create_time,update_time)" +
" values(#{username},#{password},now(),now())")
void add(String username, String password);
@Select("select * from user where username=#{username}")
User findByUserName(String username);
}
参数校验框架(Spring Validation)
在注册的时候注意加上:对注册接口的参数进行合法性校验
使用步骤:
1、引入对应的依赖:spring-boot-starter-validation
<!-- 添加validation依赖用于参数的校验 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
2、在参数前面加上@Pattern注解(@Pattern(regexp=“正则表达式(注意Java中单斜杠不行需转成\\)”))
public Result register(@Pattern(regexp = "^\\S{5,16}$") String username,@Pattern(regexp = "^\\S{5,16}$") String password)
3、在对应的Controller类上加上@Validated
@Validated
public class UserController
注意:如果遇到参数校验失败的情况的话
4、通过全局异常处理器中处理参数校验失败
@RestControllerAdvice//表示该类是一个全局异常处理类
@ExceptionHandler(Exception.class)//表示该方法可以处理所有异常
// TODO: 全局异常处理
@RestControllerAdvice
public class GlobalExceptionHandler {
// TODO: 捕获所有异常
@ExceptionHandler(Exception.class)
public Result handleException(Exception e) {
e.printStackTrace();
return Result.error(StringUtils.hasLength(e.getMessage())?e.getMessage():"参数校验失败");
}
}
三、登录功能实现
1、在数据表中查询用户
2、判断查询到的用户是否存在
3、用户存在判断密码是否正确
//TODO: 登录
@PostMapping("/login")
public Result login(@Pattern(regexp = "^\\S{5,16}$") String username,
@Pattern(regexp = "^\\S{5,16}$") String password) {
// TODO: 查询用户是否存在
User loginUser = userService.findByUserName(username);
// TODO: 判断用户是否存在
if(loginUser==null){
return Result.error("用户不存在");
}
// TODO: 判断密码是否正确
else{
if(Md5Util.getMD5String(password).equals(loginUser.getPassword())){
// 登录成功
return Result.success("jwt token令牌");
}
else {
return Result.error("密码错误");
}
}
}
登录认证:
通过令牌技术承载业务的数据,减少后续请求
查询数据库的次数,1、防止篡改
,2、保证信息的合法性和有效性
JWT令牌:JSON Web Token(通信双方通过JSON数据格式安全的传输信息)
JWT组成—通过Base64的编码格式进行编码
1、Header头部:记录令牌的类型,签名算法(用于加密算法)
2、Payload有效载荷:携带一些自定义的信息,默认信息–不能存放私密数据
3、Signature签名:防止Token被篡改,确保安全性
JWT-生成:
引入JWT的依赖
<!-- 添加JWT依赖 -->
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>4.4.0</version>
</dependency>
在单元测试中生成jwt令牌
public class JwtTest {
@Test
public void testGen(){
Map<String, Object> claims =new HashMap<>();
claims.put("id",1);
claims.put("username","admin");
//生成jwt代码
String token = JWT.create()
.withClaim("user", claims)//添加自定义信息(载荷)
.withExpiresAt(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 12))//设置过期时间为12小时
.sign(Algorithm.HMAC256("geshihua"));//指定算法,配置密钥
System.out.println(token);
}
}
jwt令牌的验证:
注意:
1、校验jwt的签名密钥要和生成jwt令牌的签名密钥吻合
2、jwt令牌解析验证报错的话那么说明对应的jwt令牌被篡改了,或者生成的jwt令牌过期失效了
//TODO:jwt令牌的验证
@Test
public void testParse(){
//定义一个字符串模拟用户传递过来的Token
String token ="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9" +
".eyJ1c2VyIjp7ImlkIjoxLCJ1c2VybmFtZSI6ImFkbWluIn0sImV4cCI6MTc0NDMyMTkyNX0" +
".wfaRNJPOvVz6sU7rsyxnyzSKdezmhyhwslp4eUW2O4g";
//调用API验证Token
JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256("geshihua")).build();
//解析Token,生成一个解析后的jwt对象
DecodedJWT decodedJWT = jwtVerifier.verify(token);
Map<String, Claim> claims = decodedJWT.getClaims();
System.out.println(claims.get("user"));
}
将JWT令牌纳入登录验证中(登录成功了之后将相关的信息存储到Token中
// 登录成功
Map<String,Object> claims=new HashMap<>();
claims.put("id",loginUser.getId());
claims.put("username",loginUser.getUsername());
String token = JwtUtil.genToken(claims);
return Result.success(token);
在其他的业务接口中解析验证Token–原理
@GetMapping("/list")
public Result list(@RequestHeader(name = "Authorization") String token, HttpServletResponse response) {
//验证Token
try {
Map<String, Object> claims = JwtUtil.parseToken(token);
} catch (Exception e) {
response.setStatus(401);
return Result.error("请先登录");
}
return Result.success("获取到对应的文章信息了!!!");
}
通过拦截器Interceptor(多个接口都需要同样的操作可以用拦截器进行实现)
完成对应的验证操作,只有通过验证的才可以进行相关的业务操作
1、先创建一个登录校验的拦截器(LoginInterceptor)–》需要在拦截器这个类上面加一个注解@Component(用于Bean对象注册)
拦截器实现HanderInterceptor
接口==>实现preHandle
方法(意味在请求的Controller方法(请求访问服务器)之前进行调用)
@Component
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//在这个拦截器中验证Token---》通过请求头中携带的的token
String token = request.getHeader("Authorization");
//解析Token
try {
Map<String, Object> claims = JwtUtil.parseToken(token);
//TODO:如果解析成功,则放行
return true;
} catch (Exception e) {
response.setStatus(401);
//TODO:如果解析失败,则返回错误信息,并且拦截
return false;
}
}
}
2、添加Interceptor拦截器到WebMvcConfig配置类中–》配置类需要通过注解@Component将配置类注册到Spring的IOC容器中去
(1)WebMvcConfig配置类需要实现WebMvcConfigurer接口
(2)配置类中将登录拦截器的Bean自动注入进来
@Autowired
private LoginInterceptor loginInterceptor;
(3)实现对应的接口方法–》用于添加对应的登录拦截器addInterceptors
@Configuration//TODO:配置类也需要注册到spring的IOC容器中
public class WebConfig implements WebMvcConfigurer {
@Autowired
private LoginInterceptor loginInterceptor;
//TODO:用于拦截器注册
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(loginInterceptor)
//TODO: 放行登录和注册接口,不进行拦截
.excludePathPatterns("/user/login","/user/register");
}
}
三、获取用户的详细信息(在进行此业务请求的时候需要再请求头中携带对象的token信息—Authorization)
根据用户名查询数据表中的用户信息(用户名的信息通过请求头中的token----Authorization获取得到)
@RequestHeader(name = "Authorization")String token
通过上面的 参数得到token携带的信息–》用户名的username,id
// TODO: 获取用户详细信息
@GetMapping("/userInfo")
public Result<User> userInfo(@RequestHeader(name = "Authorization")String token){
//根据用户名查询用户信息---》用户名是通过token解析出来的
Map<String, Object> map = JwtUtil.parseToken(token);
String username = (String) map.get("username");
User user = userService.findByUserName(username);
return Result.success(user);
}
注意:对于数据表中存在有下划线的字段的时候在进行查询的时候不会自动转化为对应的驼峰命名导致查询不到对应的信息
解决方式一:
在配置文件中开启驼峰映射
mybatis:
configuration:
map-underscore-to-camel-case: true # 开启下划线转驼峰命名 自动将数据表中有下划线字段的转换为驼峰明明形式的
解决方式二:
在写SQL语句的时候对于有下划线的字段采取起别名的方式:
@Select("select id, username, password, nickname, email, user_pic as userPic, create_time as createTime, update_time as updateTime from user where username=#{username}")