# yeb_back **Repository Path**: johndriver/yeb_back ## Basic Information - **Project Name**: yeb_back - **Description**: yeb 后端 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 2 - **Created**: 2021-06-07 - **Last Updated**: 2021-07-29 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 后端 ## swagger接口文档 ## 验证码 ```xml com.github.axet kaptcha 0.0.9 ``` 配置类 ```java @Configuration public class CaptchaConfig { @Bean public DefaultKaptcha defaultKaptcha(){ //验证码生成器 DefaultKaptcha defaultKaptcha=new DefaultKaptcha(); //配置 Properties properties = new Properties(); //是否有边框 properties.setProperty("kaptcha.border", "yes"); //设置边框颜色 properties.setProperty("kaptcha.border.color", "105,179,90"); //边框粗细度,默认为1 // properties.setProperty("kaptcha.border.thickness","1"); //验证码 properties.setProperty("kaptcha.session.key","code"); //验证码文本字符颜色 默认为黑色 properties.setProperty("kaptcha.textproducer.font.color", "blue"); //设置字体样式 properties.setProperty("kaptcha.textproducer.font.names", "宋体,楷体,微软雅黑"); //字体大小,默认40 properties.setProperty("kaptcha.textproducer.font.size", "30"); //验证码文本字符内容范围 默认为abced2345678gfynmnpwx // properties.setProperty("kaptcha.textproducer.char.string", ""); //字符长度,默认为5 properties.setProperty("kaptcha.textproducer.char.length", "4"); //字符间距 默认为2 properties.setProperty("kaptcha.textproducer.char.space", "4"); //验证码图片宽度 默认为200 properties.setProperty("kaptcha.image.width", "100"); //验证码图片高度 默认为40 properties.setProperty("kaptcha.image.height", "40"); Config config = new Config(properties); defaultKaptcha.setConfig(config); return defaultKaptcha; } } ``` ```java @RestController @Slf4j public class CaptchaController { @Autowired private DefaultKaptcha defaultKaptcha; @ApiOperation(value = "验证码") @GetMapping(value = "/captcha",produces = "image/jpeg") public void captcha(HttpServletRequest request, HttpServletResponse response){ //定义输出类型为image/jpeg response.setDateHeader("Expires",0); response.setHeader("Cache-Control","no-store,no-cache,must-revalidate"); response.addHeader("Cache-Control","post-check=0,pre-check=0"); response.setHeader("Pragma","no-cache"); response.setContentType("image/jpeg"); //---------生成验证码------------ //文本 String text = defaultKaptcha.createText(); log.info(text); request.getSession().setAttribute("captcha",text); //文本生成图形验证码 BufferedImage image = defaultKaptcha.createImage(text); ServletOutputStream outputStream =null; try { outputStream = response.getOutputStream(); //图形写入流中 ImageIO.write(image,"jpg",outputStream); outputStream.flush(); } catch (IOException e) { e.printStackTrace(); }finally { if(null!=outputStream){ try { outputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } } } ``` ## Redis优化菜单 配置 ```xml org.springframework.boot spring-boot-starter-data-redis org.apache.commons commons-pool2 ``` ```yaml spring: redis: host: 192.168.144.129 timeout: 10000ms port: 6379 database: 0 lettuce: pool: min-idle:200 max-active: 1024 max-wait: 10000ms min-idle: 5 ``` 使用 ```java @Service public class MenuServiceImpl extends ServiceImpl implements IMenuService { @Autowired private MenuMapper menuMapper; @Autowired private RedisTemplate redisTemplate; @Override public List getMenusByAdminId() { Integer adminId = ((Admin) SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getId(); //1.先去redis取数据 ValueOperations valueOperations = redisTemplate.opsForValue(); List menus = (List) valueOperations.get("menu_" + adminId); if(CollectionUtils.isEmpty(menus)){ //2.没有就查数据库,并放入redis menus = menuMapper.getMenusByAdminId(adminId); valueOperations.set("menu_" + adminId,menus); } return menus; } } ``` ## 对当前用户的限制可访问的url * 拦截获取访问url->数据库查询url(menu表)对应的可访问角色(角色表) * 对比当前用户角色是否在可访问角色中 * 在就可以访问 * 1. 用户登录后,根据id过去角色列表,并由角色列生成授权 SecurityConfig ```text //自定义登录逻辑 @Override @Bean public UserDetailsService userDetailsService(){ return username->{ //用户名获取用户信息 Admin admin = adminService.getAdminByUsername(username); if(null!=admin){ //获取角色 admin.setRoles(adminService.getRoles(admin.getId())); return admin; } throw new UsernameNotFoundException("用户名或密码错误"); }; } ``` Admin.java ```text @Override public Collection getAuthorities() { return roles.stream().map(role->new SimpleGrantedAuthority(role.getName())).collect(Collectors.toList()); } ``` 这些信息保存在上下文 * 2.拦截url得到可访问这个url角色列表 ```java @Component public class CustomFilter implements FilterInvocationSecurityMetadataSource { @Autowired private IMenuService menuService; AntPathMatcher antPathMatcher = new AntPathMatcher(); @Override public Collection getAttributes(Object object) throws IllegalArgumentException { //获取请求url String requestUrl = ((FilterInvocation) object).getRequestUrl(); List menus = menuService.getMenusWithRole(); for (Menu menu : menus) { //判断请求url与菜单角色是否匹配 if(antPathMatcher.match(menu.getUrl(),requestUrl)){ String[] str = menu.getRoles().stream().map(Role::getName).toArray(String[]::new); return SecurityConfig.createList(str); } } //没有匹配默认登录即可 return SecurityConfig.createList("ROLE_LOGIN"); } } ``` * 3.根据可范问角色列表,对比这个用户的角色(authentication中取)是否在列表中 ```java @Component public class CustomUrlDecisionManager implements AccessDecisionManager { @Override public void decide(Authentication authentication, Object o, Collection collection) throws AccessDeniedException, InsufficientAuthenticationException { for (ConfigAttribute configAttribute : collection) { String needRole = configAttribute.getAttribute(); //判断角色是否登录即可访问的角色,此角色在CustomFilter中设置。 if("ROLE_LOGIN".equals(needRole)){ if(authentication instanceof AnonymousAuthenticationToken){ throw new AccessDeniedException("尚未登录,请登录!"); }else { return; } } //判断用户角色是否为url所需角色 Collection authorities = authentication.getAuthorities(); for (GrantedAuthority authority : authorities) { if(authority.getAuthority().equals(needRole)){ return; } } } throw new AccessDeniedException("权限不足,请联系管理员"); } } ``` * 4.SecurityConfig 启用 2 3 两个类(@Autowire后使用) ```text http.authorizeRequests() //所有请求都要求认证 .anyRequest().authenticated() //添加url判断可访问角色 .withObjectPostProcessor(new ObjectPostProcessor() { @Override public O postProcess(O object) { // object.setAccessDecisionManager(customUrlDecisionManager); object.setSecurityMetadataSource(customFilter); return object; } }) ; ``` ## 职位管理 增删改查 ## 全局异常处理 ## 职称管理 增删改查 ## 权限组 ## 存储过程 * 添加部门 ```text 入参: IN `depName` varchar(32),IN `parentId` int,IN `enabled` boolean,OUT `result` int,out result2 int BEGIN -- 定义变量 DECLARE did int; DECLARE pDepPath VARCHAR(64); -- 插入一条数据 INSERT into t_department SET NAME=depName,parentId=parentId,enabled=enabled; SELECT ROW_COUNT() into result; -- 取得插入数据条数 SELECT LAST_INSERT_ID() into did; -- 取得插入数据的id SET result2=did;-- result2取得结果 id -- 取得上级部门的depPath给变量pDepPath SELECT depPath into pDepPath FROM t_department WHERE id = parentId; -- 更新当前部门的depPath CONCAT拼接字符串 UPDATE t_department SET depPath=CONCAT(pDepPath,'.',did); -- 更新上级部门的isParent UPDATE t_department SET isParent=TRUE WHERE id=parentId; END ``` * 删除部门 ```text 入参: in did int,out result int begin -- 定义变量 declare ecount int; declare pid int; declare pcount int; declare a int; -- 1.查询没有下级目录的删除目标的数量->存到a select count(*) into a from t_department where id=did and isParent=false; if a=0 then set result=-2; -- 没有的话返回-2 else -- 有的话,查询要删除的部门有多少员工->存到ecount select count(*) into ecount from t_employee where departmentId=did; if ecount>0 then set result=-1; -- 有员工返回-1 else -- 没有员工的话,可以删除,先查询上级部门的id->存到pid select parentId into pid from t_department where id=did; -- 删除 delete from t_department where id=did and isParent=false; select row_count() into result; -- 返回删除数量 -- 查询上级部门在删除本部门后子部门的数量->存到pcount select count(*) into pcount from t_department where parentId=pid; -- 如果上级部门没有子部门了,就将自己设为非上级部门 if pcount=0 then update t_department set isParent=false where id=pid; end if; end if; end if; end ``` * 使用 ```text ``` ```text ``` ## 员工管理 * 分页插件 ```text @Configuration public class MybatisPlusConfig { @Bean public PaginationInterceptor paginationInterceptor(){ return new PaginationInterceptor(); } } ``` * 分页公共返回对象 ```text public class RespPageBean { /** * 总条数 */ private Long total; /** * 数据list */ private List data; } ``` ```java @Configuration public class MybatisPlusConfig { @Bean public PaginationInterceptor paginationInterceptor(){ return new PaginationInterceptor(); } } ``` 使用(这种用法可以在xml自己编写多表复杂查找逻辑) ```text public RespPageBean getEmployeeByPage(Integer currentPage, Integer size, Employee employee, LocalDate[] beginDateScope) { //开启分页 Page page = new Page<>(currentPage,size); IPage employeeByPage = employeeMapper.getEmployeeByPage(page, employee, beginDateScope); RespPageBean respPageBean = new RespPageBean(employeeByPage.getTotal(),employeeByPage.getRecords()); return respPageBean; } ```