项目地址
项目需求地址:http://www.qiantuxueye.com/projectDetail?classId=31&indexId=118
本人项目代码仓库地址:https://gitee.com/Yuan_Ya_Jie/pingshen-test/tree/master
项目详情
本次项目耗费一周左右,鉴权使用springsecurity+jwt,文件存储使用oss等等。项目需求整体看起来不是特别难,可能MP4的文件处理与excel导入用户时的角色添加会耗费比较多的精力。
本次项目与双选项目需求基本一致,需要管理员控制比赛时间并且接口里做比赛时间的校验。双选时的解决思路是将比赛时间流程存入redis,在接口里拿出来调用做校验,现在想想真是很不妙,重复的代码太多了。所以这个项目就提供了三种解决思路,第一种是将校验包装成一个类,在接口里调用校验方法,如果比赛时间冲突则抛出异常。第二种是将校验包装成一个注解,只需要在接口上添加注解就可以完成比赛时间校验。第三种则是使用AOP完成校验。这三种校验方式后两种我平时基本不会怎么用到,所以之后会将这两种校验方法都实现一下。
项目框架及代码
项目的所有代码我已提交至个人仓库:https://gitee.com/Yuan_Ya_Jie/pingshen-test/tree/master
这里简单说一下本次项目中个人写起来不是很熟练的代码块
jwt登录校验
jwt+springsecurity的原理就是用户登录生成一个token,下次请求带着token过来会先经过jwt的过滤器,如果token有效则在jwt的过滤器中执行springsecurity的登录流程并在之后的spirngsecurity本身的过滤器中跳过校验,如果无效抛出异常,没有携带则跳过执行springsecurity本身的校验过滤器:
@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {
/**
* JWT数据的密钥
*/
private String secretKey = "fgfdsfadsfadsafdsafdsfadsfadsfdsafdasfdsafdsafdsafds4rttrefds";
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
System.out.println("JwtAuthenticationFilter.doFilterInternal()");
System.out.println("清除Spring Security上下文中的数据");
SecurityContextHolder.clearContext();
String jwt = request.getHeader("Authorization");
System.out.println("从请求头中获取到的JWT=" + jwt);
if (!StringUtils.hasText(jwt)) {
System.out.println("请求头中无JWT数据,当前过滤器将放行");
filterChain.doFilter(request, response); // 继续执行过滤器链中后续的过滤器
return; // 必须
}
// 注意:此时执行时,如果请求头中携带了Authentication,日志中将输出,且不会有任何响应,因为当前过滤器尚未放行
// 以下代码有可能抛出异常的
// TODO 密钥和各个Key应该统一定义
String username = null;
String permissionsString = null;
try {
System.out.println("请求头中包含JWT,准备解析此数据……");
Claims claims = Jwts.parser().setSigningKey(secretKey).parseClaimsJws(jwt).getBody();
username = claims.get("username").toString();
permissionsString = claims.get("permissions").toString();
System.out.println("username=" + username);
System.out.println("permissionsString=" + permissionsString);
} catch(Exception e){
return;
}
List<SimpleGrantedAuthority> permissions
= JSON.parseArray(permissionsString, SimpleGrantedAuthority.class);
System.out.println("从JWT中获取到的权限转换成Spring Security要求的类型:" + permissions);
Authentication authentication
= new UsernamePasswordAuthenticationToken(username, null, permissions);
SecurityContextHolder.getContext().setAuthentication(authentication);
System.out.println("将解析得到的用户信息传递给Spring Security");
// 放行
System.out.println("JwtAuthenticationFilter 放行");
filterChain.doFilter(request, response);
}
}
Excel生成
public void getTeacherSample(HttpServletResponse response) throws IOException {
//创建表
List<List<String>> list = ListUtils.newArrayList();
//创建表头
List<String> head0 = ListUtils.newArrayList();
List<String> head1 = ListUtils.newArrayList();
List<String> head2 = ListUtils.newArrayList();
List<String> head3 = ListUtils.newArrayList();
List<String> head4 = ListUtils.newArrayList();
head0.add(JOB_ID);
head1.add(NAME);
head2.add(SEX);
head3.add(FACULTY);
head4.add(ROLE);
list.add(head0);
list.add(head1);
list.add(head2);
list.add(head3);
list.add(head4);
//创建表内容
List<List<String>> dataList = ListUtils.newArrayList();
List<String> data = ListUtils.newArrayList();
data.add("例:12345678987");
data.add("张三");
data.add("男");
data.add("北京大学");
data.add("参赛教师");
dataList.add(data);
//使用URLEncoder,不然输出时会中文乱码
String fileName = URLEncoder.encode(TEACHER_SAMPLE, "UTF-8").replaceAll("\\+", "%20");
//设置内容的颜色
WriteCellStyle style = new WriteCellStyle();
WriteFont writeFont = new WriteFont();
writeFont.setColor(IndexedColors.RED.getIndex());
style.setWriteFont(writeFont);
HorizontalCellStyleStrategy cellStyleStrategy = new HorizontalCellStyleStrategy(new WriteCellStyle() , style);
//设置response信息
try {
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setCharacterEncoding("utf-8");
response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName);
EasyExcel.write(response.getOutputStream())
.registerWriteHandler(cellStyleStrategy)
.registerWriteHandler(new SimpleColumnWidthStyleStrategy(15))
.head(list)
.autoCloseStream(Boolean.FALSE)
.sheet("模板")
.doWrite(dataList);
} catch (Exception e) {
//重置response
response.reset();
response.setContentType("application/json");
response.setCharacterEncoding("utf-8");
//输出错误信息
R result = R.fail();
response.getWriter().println(JSON.toJSONString(result));
}
}
Excel导入用户的Listener处理
@Slf4j
public class UserListener implements ReadListener<SysUser> {
//每隔一百条存储数据
private static final int BATCH_COUNT = 100;
private List<SysUser> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
public static List<String> list = new ArrayList<>();
private final UserMapper userMapper;
private final UserService userService;
private final PasswordEncoder passwordEncoder;
private final DeptMapper deptMapper;
private final UserRoleMapper userRoleMapper;
private final RoleMapper roleMapper;
public UserListener(UserService userService , UserMapper userMapper , PasswordEncoder passwordEncoder,DeptMapper deptMapper,UserRoleMapper userRoleMapper , RoleMapper roleMapper){
this.userService = userService;
this.userMapper = userMapper;
this.passwordEncoder = passwordEncoder;
this.deptMapper = deptMapper;
this.userRoleMapper = userRoleMapper;
this.roleMapper = roleMapper;
}
@Override
public void invoke(SysUser user, AnalysisContext analysisContext) {
log.info("解析到一条数据:{}" , JSON.toJSONString(user));
if(exist(user)){
list.add(user.getUserName());
}else{
String encode = passwordEncoder.encode("123456");
user.setPassword(encode);
cachedDataList.add(user);
}
if(cachedDataList.size() >= BATCH_COUNT){
saveData();
cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
}
log.info("存储完成");
}
@Override
public void doAfterAllAnalysed(AnalysisContext analysisContext) {
if(!cachedDataList.isEmpty()){
saveData();
cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
}
log.info("所有数据解析完成!");
}
private void saveData(){
log.info("{}条数据,开始存储数据库!" , cachedDataList.size());
for (SysUser user : cachedDataList) {
SysDept dept = deptMapper.selectOne(new QueryWrapper<SysDept>().eq("dept_name", user.getDeptName()));
user.setDeptId(dept.getDeptId());
userService.save(user);
SysRole roleName = roleMapper.selectOne(new QueryWrapper<SysRole>().eq("role_name", user.getRole()));
userRoleMapper.insert(new SysUserRole(user.getUserId() , roleName.getRoleId()));
}
log.info("存储数据库成功!");
}
private boolean exist(SysUser user){
SysUser user1 = userMapper.selectOne(new QueryWrapper<SysUser>().eq("user_name" , user.getUserName()));
return user1!=null;
}
}
总结
这次项目复习了springsecurity的使用和原理,收获很大!