首页 >> 大全

SpringBoot-常见场景(下)-三更补充版

2024-01-02 大全 22 作者:考证青年

接上文(准备开始前后端分离的模式) 准备工作: 基本测试(数据库查询–可跳过,与整合步骤基本一致)

因为是前后端分离的项目,所以最终方法的返回值都会放到请求体当中—>@

所有文件与之前测试的相同

实体类

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {private Integer id;private String username;private Integer age;private String address;
}

记得加注解,才会知道这个是个接口

接口相应格式统一(另一项目的)

前端发送请求代码编写

出现了跨域问题

4.5 跨域请求 4.5.1 什么是跨域

​ 浏览器出于安全的考虑,使用 对象发起 HTTP请求时必须遵守同源策略,否则就是跨域的HTTP请求,默认情况下是被禁止的。 同源策略要求源相同才能正常进行通信,即协议、域名、端口号都完全一致。

4.5.2 CORS解决跨域

​ CORS是一个W3C标准,全称是”跨域资源共享”(Cross- ),允许浏览器向跨源服务器,发出请求,从而克服了AJAX只能同源使用的限制。

​ 它通过服务器增加一个特殊的[--Allow-]来告诉客户端跨域的限制,如果浏览器支持CORS、并且判断通过的话,就会允许发起跨域请求。

4.5.3 使用CORS解决跨域 1.使用@

可以在支持跨域的方法上或者是上加上@注解

@RestController
@RequestMapping("/user")
@CrossOrigin
public class UserController {@Autowiredprivate UserServcie userServcie;@RequestMapping("/findAll")public ResponseResult findAll(){//调用service查询数据 ,进行返回List<User> users = userServcie.findAll();return new ResponseResult(200,users);}
}

2.使用 的 方法配置(更好的方式)

@Configuration   //表所示为配置类
public class CorsConfig implements WebMvcConfigurer {@Overridepublic void addCorsMappings(CorsRegistry registry) {// 设置允许跨域的路径registry.addMapping("/**")// 设置允许跨域请求的域名.allowedOriginPatterns("*")// 是否允许cookie.allowCredentials(true)// 设置允许的请求方式.allowedMethods("GET", "POST", "DELETE", "PUT")// 设置允许的header属性.allowedHeaders("*")// 跨域允许时间.maxAge(3600);}
}

比如:

继续实现功能 前端

运用debug

最终结果

为什么要统一响应格式

data属性当中就是把我们后端响应的json字符串解析出来。

这样对前端来说判断方便很多

4.6 拦截器 4.6.0 登录案例

4.6.0.1 思路分析

​ 在前后端分离的场景中,很多时候会采用token的方案进行登录校验。

​ 登录成功时,后端会根据一些用户信息生成一个token字符串返回给前端。

​ 前端会存储这个token。以后前端发起请求时如果有token就会把token放在请求头中发送给后端。

​ 后端接口就可以获取请求头中的token信息进行解析,如果解析不成功说明token超时了或者不是正确的token,相当于是未登录状态。

​ 如果解析成功,说明前端是已经登录过的。

4.6.0.2 Token生成方案-JWT

​ 本案例采用目前企业中运用比较多的JWT来生成token。

​ 使用时先引入相关依赖

        <dependency><groupId>io.jsonwebtokengroupId><artifactId>jjwtartifactId><version>0.9.0version>dependency>

​ 然后可以使用下面的工具类来生成和解析token

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;
import java.util.Date;
import java.util.UUID;/*** JWT工具类*/
public class JwtUtil {//有效期为public static final Long JWT_TTL = 60 * 60 *1000L;// 60 * 60 *1000  一个小时//设置秘钥明文public static final String JWT_KEY = "sangeng";/*** 创建token* @param id* @param subject* @param ttlMillis* @return*/public static String createJWT(String id, String subject, Long ttlMillis) {SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;long nowMillis = System.currentTimeMillis();Date now = new Date(nowMillis);if(ttlMillis==null){ttlMillis=JwtUtil.JWT_TTL;}long expMillis = nowMillis + ttlMillis;Date expDate = new Date(expMillis);SecretKey secretKey = generalKey();JwtBuilder builder = Jwts.builder().setId(id)              //唯一的ID.setSubject(subject)   // 主题  可以是JSON数据.setIssuer("sg")     // 签发者.setIssuedAt(now)      // 签发时间.signWith(signatureAlgorithm, secretKey) //使用HS256对称加密算法签名, 第二个参数为秘钥.setExpiration(expDate);// 设置过期时间return builder.compact();}/*** 生成加密后的秘钥 secretKey* @return*/public static SecretKey generalKey() {byte[] encodedKey = Base64.getDecoder().decode(JwtUtil.JWT_KEY);SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");return key;}/*** 解析** @param jwt* @return* @throws Exception*/public static Claims parseJWT(String jwt) throws Exception {SecretKey secretKey = generalKey();return Jwts.parser().setSigningKey(secretKey).parseClaimsJws(jwt).getBody();}}

乱写就会抛出异常

4.6.0.3 登录接口实现

数据准备

DROP TABLE IF EXISTS `sys_user`;
CREATE TABLE `sys_user` (`id` int(11) NOT NULL AUTO_INCREMENT,`username` varchar(50) DEFAULT NULL,`password` varchar(50) DEFAULT NULL,PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;/*Data for the table `sys_user` */insert  into `sys_user`(`id`,`username`,`password`) values (1,'root','root'),(2,'sangeng','caotang');

实体类

@Data
@NoArgsConstructor
@AllArgsConstructor
public class SystemUser {private Integer id;private String username;private String password;
}

import com.sangeng.domain.ResponseResult;
import com.sangeng.domain.SystemUser;
import com.sangeng.service.SystemUserService;
import com.sangeng.utils.JwtUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.util.HashMap;
import java.util.Map;
import java.util.UUID;@RestController
@RequestMapping("/sys_user")
public class SystemUserController {@Autowiredprivate SystemUserService userService;@PostMapping("/login")public ResponseResult login(@RequestBody SystemUser user) {//校验用户名密码是否正确SystemUser loginUser = userService.login(user);Map<String, Object> map;if (loginUser != null) {//如果正确 生成token返回map = new HashMap<>();String token = JwtUtil.createJWT(UUID.randomUUID().toString(), String.valueOf(loginUser.getId()), null);map.put("token", token);} else {//如果不正确 给出相应的提示return new ResponseResult(300, "用户名或密码错误,请重新登录");}return new ResponseResult(200, "登录成功", map);}
}

public interface SystemUserService {public SystemUser login(SystemUser user);
}
@Service
public class SystemUserServcieImpl implements SystemUserService {@Autowiredprivate SystemUserMapper systemUserMapper;@Overridepublic SystemUser login(SystemUser user) {SystemUser loginUser = systemUserMapper.login(user);return loginUser;}
}

dao

@Mapper
@Repository
public interface UserMapper {List<User> findAll();
}


DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.sangeng.mapper.SystemUserMapper"><select id="login" resultType="com.sangeng.domain.SystemUser">select * from sys_user where username = #{username} and password = #{password}select>
mapper>

动画片常见场景_场景速写常见题目_

4.6.0.4 登录页面


<head><!-- Meta--><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0"><meta name="description" content=""><meta name="keywords" content=""><meta name="author" content=""><title>BeAdmin - Bootstrap Admin Theme</title><!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries --><!--[if lt IE 9]><script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script><script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script><![endif]--><!-- Bootstrap CSS--><link rel="stylesheet" href="../app/css/bootstrap.css"><!-- Vendor CSS--><link rel="stylesheet" href="../vendor/fontawesome/css/font-awesome.min.css"><link rel="stylesheet" href="../vendor/animo/animate+animo.css"><!-- App CSS--><link rel="stylesheet" href="../app/css/app.css"><link rel="stylesheet" href="../app/css/common.css"><!-- Modernizr JS Script--><script src="../vendor/modernizr/modernizr.js" type="application/javascript"></script><!-- FastClick for mobiles--><script src="../vendor/fastclick/fastclick.js" type="application/javascript"></script>
</head><body><!-- START wrapper--><div class="row row-table page-wrapper" id="app"><div class="col-lg-3 col-md-6 col-sm-8 col-xs-12 align-middle"><!-- START panel--><div data-toggle="play-animation" data-play="fadeIn" data-offset="0" class="panel panel-dark panel-flat"><div class="panel-heading text-center"><a href="login.html#"><img src="../app/img/logo.png" alt="Image" class="block-center img-rounded"></a><p class="text-center mt-lg"><strong>SIGN IN TO CONTINUE.</strong></p></div><div class="panel-body"><form role="form" class="mb-lg"><div class="text-right mb-sm"><a href="login.html#" class="text-muted">Need to Signup?</a></div><div class="form-group has-feedback"><input id="exampleInputEmail1" v-model="user.username" type="email" placeholder="请输入用户名" class="form-control"><span class="fa fa-envelope form-control-feedback text-muted"></span></div><div class="form-group has-feedback"><input id="exampleInputPassword1" v-model="user.password"  type="password" placeholder="请输入密码" class="form-control"><span class="fa fa-lock form-control-feedback text-muted"></span></div><div class="clearfix"><div class="checkbox c-checkbox pull-left mt0"><label><input type="checkbox" value=""><span class="fa fa-check"></span>Remember Me</label></div><div class="pull-right"><a href="login.html#" class="text-muted">Forgot your password?</a></div></div><button type="submit" class="btn btn-block btn-primary" @click="handleLogin()">登录</button></form></div></div><!-- END panel--></div></div><!-- END wrapper--><!-- START Scripts--><!-- Main vendor Scripts--><script src="../vendor/jquery/jquery.min.js"></script><script src="../vendor/bootstrap/js/bootstrap.min.js"></script><!-- Animo--><script src="../vendor/animo/animo.min.js"></script><!-- Custom script for pages--><script src="../app/js/pages.js"></script><script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script><script src="https://unpkg.com/axios/dist/axios.min.js"></script><script>var v = new Vue({el:"#app",data:{user:{}},methods:{handleLogin(){//请求后台登录接口axios.post("http://localhost/sys_user/login",this.user).then((res)=>{// console.log(res);//判断是否成功if(res.data.code==200){//登录成功// alert("登录成功");//存储tokenlocalStorage.setItem("token",res.data.data.token);//跳转页面到index.htmllocation.href = "/index.html";}else{//登录失败alert(res.data.msg);}})}}});</script><!-- END Scripts-->
</body></html>

4.6.1 拦截器的概念(和MVC基本无异)

​ 如果我们想在多个方法执行之前或者之后都进行一些处理,甚至某些情况下需要拦截掉,不让方法执行。那么可以使用为我们提供的拦截器。

​ 详情见 课程中拦截器相关章节。

4.6.1 使用步骤 ①创建类实现接口

public class LoginInterceptor implements HandlerInterceptor {
}

②实现方法

@Component  //把类放到spring容器里面,底下为相应的业务逻辑
public class LoginInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {//获取请求头中的tokenString token = request.getHeader("token");//判断token是否为空,如果为空也代表未登录 提醒重新登录(401)if(!StringUtils.hasText(token)){response.sendError(HttpServletResponse.SC_UNAUTHORIZED);return false;}//解析token看看是否成功try {Claims claims = JwtUtil.parseJWT(token);String subject = claims.getSubject();System.out.println(subject);} catch (Exception e) {e.printStackTrace();//如果解析过程中没有出现异常说明是登录状态//如果出现了异常,说明未登录,提醒重新登录(401)response.sendError(HttpServletResponse.SC_UNAUTHORIZED);return false;}return true;}
}

③配置拦截器

@Configuration
public class LoginConfig implements WebMvcConfigurer {@Autowiredprivate LoginInterceptor loginInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(loginInterceptor)//添加拦截器.addPathPatterns("/**")  //配置拦截路径.excludePathPatterns("/sys_user/login");//配置排除路径}
}

4.7 异常统一处理(整合MVC的) ①创建类加上@注解进行标识

@ControllerAdvice
public class MyControllerAdvice {}

②定义异常处理方法

​ 定义异常处理方法,使用 @ 标识可以处理的异常。

@ControllerAdvice
public class MyControllerAdvice {@ExceptionHandler(RuntimeException.class)@ResponseBodypublic ResponseResult handlerException(Exception e){//获取异常信息,存放如ResponseResult的msg属性String message = e.getMessage();ResponseResult result = new ResponseResult(300,message);//把ResponseResult作为返回值返回,要求到时候转换成json存入响应体中return result;}
}

4.8 获取web原生对象

​ 我们之前在web阶段我们经常要使用到对象,,对象等。我们也可以通过获取到这些对象。(不过在MVC中我们很少获取这些对象,因为有更简便的方式,避免了我们使用这些原生对象相对繁琐的API。)

​ 我们只需要在方法上添加对应类型的参数即可,但是注意数据类型不要写错了,会把我们需要的对象传给我们的形参。

@RestController
public class TestController {@RequestMapping("/getRequestAndResponse")public ResponseResult getRequestAndResponse(HttpServletRequest request, HttpServletResponse response, HttpSession session){System.out.println(request);return new ResponseResult(200,"成功");}
}

4.9 自定义参数解析

所有方法都用,这样就很麻烦。

​ 如果我们想实现像获取请求体中的数据那样,在方法的参数上增加一个@注解就可以获取到对应的数据的话。

​ 可以使用来实现自定义的参数解析。

新建一个包

①定义用来标识的注解

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface CurrentUserId {}

②创建类实现接口并重写其中的方法

注意加上@注解注入容器

@Component
public class UserIdArgumentResolver implements HandlerMethodArgumentResolver {//判断方法参数使用能使用当前的参数解析器进行解析@Overridepublic boolean supportsParameter(MethodParameter parameter) {//如果方法参数有加上CurrentUserId注解,就能把被我们的解析器解析return parameter.hasParameterAnnotation(CurrentUserId.class);}//进行参数解析的方法,可以在方法中获取对应的数据,然后把数据作为返回值返回。方法的返回值就会赋值给对应的方法参数@Overridepublic Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {//获取请求头中的tokenString token = webRequest.getHeader("token");if(StringUtils.hasText(token)){//解析token,获取userIdClaims claims = JwtUtil.parseJWT(token);String userId = claims.getSubject();//返回结果return userId;}return null;}
}

③配置参数解析器,因为 这种参数解析器属于MVC中的主键,我们要进行主键相关的配置。就像之前的

@Configuration
public class ArgumentResolverConfig implements WebMvcConfigurer {@Autowiredprivate UserIdArgumentResolver userIdArgumentResolver;@Overridepublic void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {resolvers.add(userIdArgumentResolver);}
}

④测试

在需要获取的方法中增加对应的方法参数然后使用@进行标识即可获取到数据

@RestController
@RequestMapping("/user")
//@CrossOrigin
public class UserController {@Autowiredprivate UserServcie userServcie;@RequestMapping("/findAll")public ResponseResult findAll(@CurrentUserId String userId) throws Exception {System.out.println(userId);//调用service查询数据 ,进行返回sList<User> users = userServcie.findAll();return new ResponseResult(200,users);}
}

关于我们

最火推荐

小编推荐

联系我们


版权声明:本站内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 88@qq.com 举报,一经查实,本站将立刻删除。备案号:桂ICP备2021009421号
Powered By Z-BlogPHP.
复制成功
微信号:
我知道了