Javaweb实战项目:公司信息管理系统(下)
补充@ 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语句:
<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并加上
@Autowired
private AliOSSProperties aliOSSProperties;
然后再在方法里定义String bucketName=aliOSSProperties.getBucketName();
—-登录系统—-#
在Controller层创建一个 LoginController类专门用来登录,里面加上基本的@RestController等注解,然后登录请求方式为POST,需要传递Emp的参数,就Emp e=empService.login(emp);最后返回值为:(然后跟以前一样依样画葫芦)
return e!=null?Result.success():Result.error("用户名或密码错误");
EmpMapper里面的Select语句:
@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
先来看代码:
@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代码里面解码:
@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软件包下粘贴下面的代码:(就是刚刚写的东西)
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 接口改成:
@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 = “/*”)是指拦截全部请求。然后限制运行再去请求直接全部拦截了。
所以就需要添加放行操作:
filterChain.doFilter(servletRequest,servletResponse);//放行
一个WEB程序可以有多个filter,执行循序根据类型顺序(A,B,C…排下去)。
应用到案例当中去:
先看源码(类在filter软件包下)
@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。
@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,然后里面编写:
@RestControllerAdvice
public class GlobalException {
@ExceptionHandler(Exception.class)
public Result ex(Exception exception){
exception.printStackTrace();
return Result.error("操作失败,请联系何平安~");
}
}
Exception.class是指捕获全部异常,也可以改成其他的什么IOEx啥的。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?