本文章的图片由于是天香园1.0的但是过期了图片就查看不了,就当我自己的一个纪念了吧~
** **补充@ RequestBody注解:@RequestBody的使用_justry_deng的博客-CSDN博客 ,四种请求方式的区别:总结get、put、post、delete的区别和用法_get post put delete_云庄clouder的博客-CSDN博客
查询员工
跟之前查询部门一样的,直接EmpController写@GetMapping,Integer id,Emp emp=empService.getById(id);success(emp),然后后面就根据idea的提示非常简单。补充一下@RequestBody注解是将数据变为json格式的。
修改员工
在查询员工界面显示出来的修改数据
@PutMapping+@RequestBody(json格式数据传递)+update数据库语句+Service层自动补充更新时间变量。
XML中SQL语句:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 <update id="update"> update emp <set> <if test="username != null and username != ''"> username = #{username}, </if> <if test="password != null and password != ''"> password = #{password}, </if> <if test="name != null and name != ''"> name = #{name}, </if> <if test="gender != null"> gender = #{gender}, </if> <if test="image != null and image != ''"> image = #{image}, </if> <if test="job != null"> job = #{job}, </if> <if test="entrydate != null"> entrydate = #{entrydate}, </if> <if test="deptId != null"> dept_id = #{deptId}, </if> <if test="updateTime != null"> update_time = #{updateTime} </if> </set> where id = #{id} </update>
配置文件
方式一:
还是为了方便操作,由于一个一个在类里面改参数很难找,所以就直接将参数定义在一个配置文件里面方便修改,比如OSS的accessKeyId,accessKeySecret,就用SpringBoot自己生成的application.properties拿来配置,比如我要配置阿里云Access的ID:
application.properties里面:aliyun.oss.accessKeyId=xxxxxxxx
然后再在AliOSSutils类里面将定义的accessKeyId的值去掉,并在上面加上@Value(“${aliyun.oss.accessKeyId}”)
方式二:
使用yml进行配置,在resources文件夹下application.yml文件,yml配置时需要缩进,比如我要配置服务器端口号:
server: port: 9000
@ConfigurationProperties注解
就是不用一个一个写@Value注解了,但是没多大用除非变量很多。方法就是在对应类的包下创建一个类,里面再定义private变量并加上@Date,@Component,@ConfigurationProperties(prefix = “aliyun.oss”)这里的prefix里面写配置文件里面的最后一个逗号(或yml里面的冒号)的前面的,比如我这里配置的是AliOSSUtils里面的bucketName,然后再在AliOSSUtils里面去掉原来的 bucketName并加上
1 2 @Autowired private AliOSSProperties aliOSSProperties;
然后再在方法里定义String bucketName=aliOSSProperties.getBucketName();
—-登录系统 —- 在Controller层创建一个 LoginController类专门用来登录,里面加上基本的@RestController等注解,然后登录请求方式为POST,需要传递Emp的参数,就Emp e=empService.login(emp);最后返回值为:(然后跟以前一样依样画葫芦)
1 return e!=null?Result.success():Result.error("用户名或密码错误");
EmpMapper里面的Select语句:
1 @Select("select *from emp where username=#{username} and password=#{password}")
—登录校验 — 防止登录后再次登录不用输密码,就需要登录校验。使用统一拦截技术可以不用在每个接口都写 if语句,
会话技术 :用户打开浏览器访问服务器,会话建立,直到一方断开连接,会话结束,在一次会话中可以包含多次请求和响应。
会话跟踪 :维护浏览器状态的方法,服务器需要识别多次请求是否来自于同一浏览器,以便同一次会话的多次请求间共享数据。
会话跟踪技术防止白雪,直接不学原始的Cookie和Session方式,直接学JWT令牌技术 。
Maven接口下载:Maven Repository: io.jsonwebtoken » jjwt » 0.9.1 (mvnrepository.com)
JWT官网:JSON Web Tokens – jwt.io
先来看代码:
1 2 3 4 5 6 7 8 9 10 11 12 @Test public void testJWT(){ Map<String, Object> cliams=new HashMap<>(); cliams.put("id",1); cliams.put("name","何平安"); String jwt=Jwts.builder() .signWith(SignatureAlgorithm.HS256,"hepingan")//签名算法 .setClaims(cliams)//自定义内容 .setExpiration(new Date(System.currentTimeMillis()+3600*100))//设置令牌有效期为1h .compact(); System.out.println(jwt); }
三个参数,第一个signWith参数直接输HS256回车自动生成,这是JWT官网提供的算法生成,也有其他类型的算法看官网了,后面那个是密钥 。
setClaims里面输入自定义内容,可以是Map集合,然后算法会自动将他转换成字符,setExpiration设置令牌有效期,最后加上.compact();输出后结果复制到JWT官网就可以解码了:
在Java代码里面解码:
1 2 3 4 5 6 7 8 @Test public void testParseJWT(){ Claims claims= Jwts.parser() .setSigningKey("hepingan")//密钥 .parseClaimsJws("eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoi5L2V5bmz5a6JIiwiaWQiOjEsImV4cCI6MTY5Mjk3MjYxOH0.gJmcR4cuPNWU9hmuLXm8ucpAJFqZNTe_oqstc0Cb-9k") .getBody(); System.out.println(claims); }
用JWT自带的Claims类来输出数据, 第一个是密钥,然后是码,getBody是获取整个码(getHeader是获取算法方法)。
应用到项目中
在utils软件包下粘贴下面的代码:(就是刚刚写的东西)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; import java.util.Date; import java.util.Map; public class JwtUtils { private static String signKey = "itheima"; private static Long expire = 43200000L; /** * 生成JWT令牌 * @param claims JWT第二部分负载 payload 中存储的内容 * @return */ public static String generateJwt(Map<String, Object> claims){ String jwt = Jwts.builder() .addClaims(claims) .signWith(SignatureAlgorithm.HS256, signKey) .setExpiration(new Date(System.currentTimeMillis() + expire)) .compact(); return jwt; } /** * 解析JWT令牌 * @param jwt JWT令牌 * @return JWT第二部分负载 payload 中存储的内容 */ public static Claims parseJWT(String jwt){ Claims claims = Jwts.parser() .setSigningKey(signKey) .parseClaimsJws(jwt) .getBody(); return claims; } }
然后再在之前的login 接口改成:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @PostMapping("/login") public Result login(@RequestBody Emp emp){ log.info("员工登录:{}",emp); Emp e=empService.login(emp); if(e!=null){ Map<String, Object> claims=new HashMap<>(); claims.put("id",emp.getId()); claims.put("name",emp.getName()); String jwt=JwtUtils.generateJwt(claims); return Result.success(jwt); } return Result.error("用户名或密码错误"); }
这样在以后每一次请求时都会先看 JWT令牌,按F12–>网络–>请求–>dept里找到Token就可以看到令牌了。
—Filter— 过滤器,可以把请求拦截下来,只有经过过滤器才能请求。(就是网络安全类的)
先定义一个 Filter类,然后加上@WebFilter注解,配置拦截资源路径,引导类(启动类)加上@ServletComponentScan注解开启Servlet组件支持。
Filter 类单独创建个包在top.hepingan下吧,implements的Filter记住要选 servlet,(import jakarta.servlet.*;)然后直接实现三个方法。
@WebFilter(urlPatterns = “/*”)是指拦截全部请求。然后限制运行再去请求直接全部拦截了。
所以就需要添加放行操作:
1 filterChain.doFilter(servletRequest,servletResponse);//放行
一个WEB程序可以有多个filter,执行循序根据类型顺序(A,B,C…排下去)。
应用到案例当中去:
先看源码(类在filter软件包下)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 @Slf4j @WebFilter(urlPatterns = "/*") public class LoginCheckFilter implements Filter { @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest request=(HttpServletRequest) servletRequest; HttpServletResponse response=(HttpServletResponse) servletResponse; //获取请求url String url= request.getRequestURL().toString(); log.info("请求的url:"+url); //判断url是否存在login,如果有放行 if (url.contains("login")){ log.info("登录操作,放行..."); filterChain.doFilter(request,response); return; } //判断令牌是否存在 String jwt=request.getHeader("token"); if(!StringUtils.hasLength(jwt)){ log.info("请求头token为空,返回未登录的信息"); Result error=Result.error("NOT_LOGIN"); String notLogin=JSONObject.toJSONString(error); response.getWriter().write(notLogin); return; } //解析 token try { JwtUtils.parseJWT(jwt); }catch (Exception e){ e.printStackTrace(); log.info("解析令牌失败,返回未登录错误信息"); Result error=Result.error("NOT_LOGIN"); String notLogin=JSONObject.toJSONString(error); response.getWriter().write(notLogin); return; } log.info("令牌合法,放行"); filterChain.doFilter(request,response); } }
先获取请求的url,就是请求方法的https地址,然后解析里面有没有login,因为登录操作不用解析令牌就直接放行,然后解析令牌头是否存在,如果存在就解析整个令牌,如果令牌合法就放行。错误则给前端返回”NOT_LOGIN”.
—Interceptor– 拦截器,跟过滤器差不多的,用来动态拦截控制器的执行。
快速入门
先定义拦截器:创建类:Interceptor.LoginCheckInterceptor,接口:HandlerInterceptor,然后按crtrl+O重写全部方法,第一个是目标资源方法运行前放行 ,第二个是标资源方法运行后运行 ,第三个是视图渲染完毕后运行,最后运行 。然后再在top.hepingan下创建类:config.WebConfig,里面接口:WebMvcConfiguer,然后实现方法:addInterceptors,然后注册一个拦截器:private LoginCheckInterceptor loginCheckInterceptor;然后再在实现方法里面改为:registry.addInterceptor(loginCheckInterceptor).addPathPatterns(“/**”);
注意在Interceptor里面调用全部请求为/**,Filter里面为/*,然后将之前的 Filter的@WebFilter注释掉防止冲突,最后启动项目,就之前Filter那个 login请求发送就ok。
拦截路径 :‘/*’为一级路径,只能拦截/dept,/emp的请求,/dept/1就不能拦截。’/**’为全部拦截,啥都能匹配。/depts/**则是/depts下的全部路径,/depts/*则是depts下的一级路径。
Filter与Interceptor的关系:
实操到项目
只修改Interceptor的第一个实现方法:跟Filter的差不多,先复制过来然后去掉定义的Http的变量,然后放行就把return值设为true,不放行设置为false。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 @Override//目标资源方法运行前放行 public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("preHandle..."); //获取请求url String url= request.getRequestURL().toString(); log.info("请求的url:"+url); //判断url是否存在login,如果有放行 if (url.contains("login")){ log.info("登录操作,放行..."); return true; } //判断令牌是否存在 String jwt=request.getHeader("token"); if(!StringUtils.hasLength(jwt)){ log.info("请求头token为空,返回未登录的信息"); Result error=Result.error("NOT_LOGIN"); String notLogin= JSONObject.toJSONString(error); response.getWriter().write(notLogin); return false; } //解析 token try { JwtUtils.parseJWT(jwt); }catch (Exception e){ e.printStackTrace(); log.info("解析令牌失败,返回未登录错误信息"); Result error=Result.error("NOT_LOGIN"); String notLogin=JSONObject.toJSONString(error); response.getWriter().write(notLogin); return false; } log.info("令牌合法,放行"); return true; }
这个时候再去访问网址就直接返回到登录界面了。
—异常处理— 目前存在的异常:往新增部门新增相同的名字的部门,会发现浏览器没反应(原因就是SQL语句创建name的时候已设置为unique,不能重复)。
这时就往top.hepingan包下创建类Exception.GlobalException,然后里面编写:
1 2 3 4 5 6 7 8 @RestControllerAdvice public class GlobalException { @ExceptionHandler(Exception.class) public Result ex(Exception exception){ exception.printStackTrace(); return Result.error("操作失败,请联系何平安~"); } }
Exception.class是指捕获全部异常,也可以改成其他的什么IOEx啥的。