当前位置: 首页 > news >正文

php网站开发公司武汉建站公司

php网站开发公司,武汉建站公司,订阅号上链接的网站怎么做的,酒店网站开发需求是企业写的吗目录 1. 数据库设计 2. 代码设计 登录认证过滤器 认证成功处理器AuthenticationSuccessHandler 认证失败处理器AuthenticationFailureHandler AuthenticationEntryPoint配置 AccessDeniedHandler配置 UserDetailsService配置 Token校验过滤器 登录认证过滤器接口配置…

目录

1. 数据库设计

2. 代码设计

登录认证过滤器

认证成功处理器AuthenticationSuccessHandler

认证失败处理器AuthenticationFailureHandler

AuthenticationEntryPoint配置

AccessDeniedHandler配置

UserDetailsService配置

Token校验过滤器

登录认证过滤器接口配置

Spring Security全局配置

util包

测试结果


在SpringSecurity实现前后端分离登录token认证详解_springsecurity前后端分离登录认证-CSDN博客基础上进行重构,实现前后端分离架构登录认证,基本思想相同,借鉴开源Gitee代码进行改造,具有更好的代码规范。

1. 数据库设计

DROP TABLE IF EXISTS `t_auth`;
CREATE TABLE `t_auth`  (`id` BIGINT(11) NOT NULL AUTO_INCREMENT,`name` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '名称',`url` VARCHAR(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '路径',`status` INT(1) NULL DEFAULT NULL,`create_time` DATETIME(0) NULL DEFAULT NULL,`update_time` TIMESTAMP(0) NULL DEFAULT CURRENT_TIMESTAMP,PRIMARY KEY (`id`) USING BTREE
) ENGINE = INNODB AUTO_INCREMENT = 7 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = COMPACT;-- ----------------------------
-- Records of t_auth
-- ----------------------------
INSERT INTO `t_auth` VALUES (1, '删除用户', '/usr/del', 1, '2021-11-26 17:08:11', '2021-11-26 17:07:52');
INSERT INTO `t_auth` VALUES (2, '新增用户', '/usr/add', 1, '2021-11-26 17:08:13', '2021-11-26 17:08:09');
INSERT INTO `t_auth` VALUES (3, '添加产品', '/product/add', 1, '2021-11-26 17:08:42', '2021-11-26 17:08:29');
INSERT INTO `t_auth` VALUES (4, '下架产品', '/product/del', NULL, NULL, '2021-11-26 17:12:17');
INSERT INTO `t_auth` VALUES (5, '注册', '/user/register', NULL, NULL, '2021-11-26 17:13:32');
INSERT INTO `t_auth` VALUES (6, '注销', '/user/logOff', NULL, NULL, '2021-11-26 17:13:50');-- ----------------------------
-- Table structure for t_role
-- ----------------------------
DROP TABLE IF EXISTS `t_role`;
CREATE TABLE `t_role`  (`id` BIGINT(11) NOT NULL,`name` VARCHAR(60) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '角色名称',`status` INT(1) NULL DEFAULT NULL,`create_time` DATETIME(0) NULL DEFAULT NULL,`update_time` TIMESTAMP(0) NULL DEFAULT CURRENT_TIMESTAMP,PRIMARY KEY (`id`) USING BTREE
) ENGINE = INNODB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = COMPACT;-- ----------------------------
-- Records of t_role
-- ----------------------------
INSERT INTO `t_role` VALUES (1, 'ROLE_admin', 1, '2021-11-26 17:08:52', '2021-11-26 17:08:51');
INSERT INTO `t_role` VALUES (2, 'ROLE_dba', 1, '2021-11-26 17:09:10', '2021-11-26 17:09:05');
INSERT INTO `t_role` VALUES (3, 'ROLE_vip', 1, '2021-11-26 17:09:32', '2021-11-26 17:09:25');
INSERT INTO `t_role` VALUES (4, 'ROLE_user', 1, '2021-11-26 17:09:45', '2021-11-26 17:09:42');-- ----------------------------
-- Table structure for t_role_auth
-- ----------------------------
DROP TABLE IF EXISTS `t_role_auth`;
CREATE TABLE `t_role_auth`  (`id` BIGINT(20) NOT NULL AUTO_INCREMENT,`role_id` BIGINT(20) NULL DEFAULT NULL,`auth_id` BIGINT(20) NULL DEFAULT NULL,`status` INT(1) NULL DEFAULT NULL,`create_time` DATETIME(0) NULL DEFAULT NULL,`update_time` TIMESTAMP(0) NULL DEFAULT CURRENT_TIMESTAMP,PRIMARY KEY (`id`) USING BTREE
) ENGINE = INNODB AUTO_INCREMENT = 5 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = COMPACT;-- ----------------------------
-- Records of t_role_auth
-- ----------------------------
INSERT INTO `t_role_auth` VALUES (1, 1, 3, 1, '2021-11-26 17:11:31', '2021-11-26 17:11:29');
INSERT INTO `t_role_auth` VALUES (2, 1, 4, 1, '2021-11-26 17:11:31', '2021-11-26 17:11:29');
INSERT INTO `t_role_auth` VALUES (3, 4, 5, 1, '2021-11-26 17:14:45', '2021-11-26 17:14:35');
INSERT INTO `t_role_auth` VALUES (4, 4, 6, 1, '2021-11-26 17:14:47', '2021-11-26 17:14:41');-- ----------------------------
-- Table structure for t_user
-- ----------------------------
DROP TABLE IF EXISTS `t_user`;
CREATE TABLE `t_user`  (`id` BIGINT(11) NOT NULL,`user_id` VARCHAR(60) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '唯一的userId',`username` VARCHAR(60) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '用户名',`password` VARCHAR(60) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '密码',`name` VARCHAR(60) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '姓名',`status` INT(1) NULL DEFAULT NULL,`create_time` DATETIME(0) NULL DEFAULT NULL,`update_time` TIMESTAMP(0) NULL DEFAULT CURRENT_TIMESTAMP,PRIMARY KEY (`id`) USING BTREE
) ENGINE = INNODB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = COMPACT;-- ----------------------------
-- Records of t_user
-- ----------------------------
INSERT INTO `t_user` VALUES (1, '120', 'zhangsan', '123456', '张三', 1, '2021-11-26 17:07:03', '2021-11-26 17:06:53');
INSERT INTO `t_user` VALUES (2, '110', 'lisi', '123456', '李四', 1, '2021-11-26 17:07:36', '2021-11-26 17:07:12');-- ----------------------------
-- Table structure for t_user_role
-- ----------------------------
DROP TABLE IF EXISTS `t_user_role`;
CREATE TABLE `t_user_role`  (`id` BIGINT(20) NOT NULL AUTO_INCREMENT,`user_id` VARCHAR(60) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL COMMENT '用户唯一userId',`role_id` BIGINT(20) NULL DEFAULT NULL,`status` INT(1) NULL DEFAULT NULL,`create_time` DATETIME(0) NULL DEFAULT NULL,`update_time` TIMESTAMP(0) NULL DEFAULT CURRENT_TIMESTAMP,PRIMARY KEY (`id`) USING BTREE
) ENGINE = INNODB AUTO_INCREMENT = 3 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = COMPACT;-- ----------------------------
-- Records of t_user_role
-- ----------------------------
INSERT INTO `t_user_role` VALUES (1, '120', 1, 1, '2021-11-26 17:10:10', '2021-11-26 17:10:11');
INSERT INTO `t_user_role` VALUES (2, '110', 2, 1, '2021-11-26 17:11:16', '2021-11-26 17:11:13');

2. 代码设计

登录认证过滤器

Spring Security默认的表单登录认证的过滤器是UsernamePasswordAuthenticationFilter,这个过滤器并不适用于前后端分离的架构,因此我们需要自定义一个过滤器。参照UsernamePasswordAuthenticationFilter这个过滤器改造一下。

/*** 登录认证的filter,参照UsernamePasswordAuthenticationFilter,添加到这之前的过滤器*/public class JwtAuthenticationLoginFilter extends AbstractAuthenticationProcessingFilter {/*** 构造方法,调用父类的,设置登录地址/login,请求方式POST*/public JwtAuthenticationLoginFilter() {super(new AntPathRequestMatcher("/login", "POST"));}@Overridepublic Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) {//获取表单提交数据String username = request.getParameter("username");String password = request.getParameter("password");//封装到token中提交UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username,password);return getAuthenticationManager().authenticate(authRequest);}
}

认证成功处理器AuthenticationSuccessHandler

上述的过滤器接口一旦认证成功,则会调用AuthenticationSuccessHandler进行处理,因此我们可以自定义一个认证成功处理器进行自己的业务处理,代码如下:

@Component
public class LoginAuthenticationSuccessHandler implements AuthenticationSuccessHandler {@Autowiredprivate JwtUtil jwtUtil;@AutowiredRedisTemplate redisTemplate;@Overridepublic void onAuthenticationSuccess(HttpServletRequest httpServletRequest,HttpServletResponse httpServletResponse,Authentication authentication) throws IOException {UserDetails userDetails = (UserDetails) authentication.getPrincipal();SecurityContextHolder.getContext().setAuthentication(authentication);Map<String,String> map=new HashMap<>();map.put("username",userDetails.getUsername());//jwt生成tokenString token = jwtUtil.getToken(map);RedisUser redisUser = RedisUser.builder().username(userDetails.getUsername()).password(userDetails.getPassword()).authorities(userDetails.getAuthorities().stream().map(i->i.getAuthority()).collect(Collectors.toList())).build();//将用户信息保存到redis缓存中redisTemplate.opsForValue().set(userDetails.getUsername(),redisUser,12, TimeUnit.HOURS);ResponseUtils.result(httpServletResponse,new ResultMsg(200,"登录成功!",token));}}

认证失败处理器AuthenticationFailureHandler

同样的,一旦登录失败,比如用户名或者密码错误等等,则会调用AuthenticationFailureHandler进行处理,因此我们需要自定义一个认证失败的处理器,其中根据异常信息返回特定的JSON数据给客户端,代码如下:

@Component
public class LoginAuthenticationFailureHandler implements AuthenticationFailureHandler {/*** 一旦登录失败则会被调用*/@Overridepublic void onAuthenticationFailure(HttpServletRequest httpServletRequest,HttpServletResponse response,AuthenticationException exception) throws IOException {//TODO 根据项目需要返回指定异常提示,这里演示了一个用户名密码错误的异常//BadCredentialsException 这个异常一般是用户名或者密码错误if (exception instanceof BadCredentialsException){ResponseUtils.result(response,new ResultMsg(200,"用户名或密码不正确!",null));}ResponseUtils.result(response,new ResultMsg(200,"登录失败",null));}
}

AuthenticationEntryPoint配置

AuthenticationEntryPoint这个接口当用户未通过认证访问受保护的资源时,将会调用其中的commence()方法进行处理。

@Component
@Slf4j
public class EntryPointUnauthorizedHandler implements AuthenticationEntryPoint {@Overridepublic void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException {ResponseUtils.result(response,new ResultMsg(403,"认证失败,请重新登录!",null));}
}

AccessDeniedHandler配置

AccessDeniedHandler这处理器当认证成功的用户访问受保护的资源,但是权限不够,则会进入这个处理器进行处理。

@Component
public class RequestAccessDeniedHandler implements AccessDeniedHandler {@Overridepublic void handle(HttpServletRequest request,HttpServletResponse response,AccessDeniedException accessDeniedException) throws IOException {ResponseUtils.result(response,new ResultMsg(403,"权限不足!",null));}
}

UserDetailsService配置

UserDetailsService这个类是用来加载用户信息,包括用户名、密码、权限、角色集合,我们需要实现这个接口,从数据库加载用户信息,代码如下:

@Service
public class JwtTokenUserDetailsService implements UserDetailsService {/*** 查询用户详情的service*/@Autowiredprivate LoginService loginService;@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {//从数据库中查询SecurityUser securityUser = loginService.loadByUsername(username);System.out.println(securityUser);//用户不存在直接抛出UsernameNotFoundException,security会捕获抛出BadCredentialsExceptionif (Objects.isNull(securityUser))throw new UsernameNotFoundException("用户不存在!");return securityUser;}
}

其中的LoginService是根据用户名从数据库中查询出密码、角色、权限,代码如下:

@Service
public class LoginServiceImpl implements LoginService {@Autowiredprivate PasswordEncoder passwordEncoder;@AutowiredTUserService tUserService;@AutowiredTRoleService tRoleService;@Nullable@Overridepublic SecurityUser loadByUsername(String username) {//获取用户信息TUser user = tUserService.getByUsername(username);if (Objects.nonNull(user)){SecurityUser securityUser = new SecurityUser();securityUser.setUsername(username);//todo 此处为了方便,直接在数据库存储的明文,实际生产中应该存储密文,则这里不用再次加密securityUser.setPassword(passwordEncoder.encode(user.getPassword()));//查询该用户的角色List<String> userRoles = tRoleService.selectAllByUsername(username);String[] a={};List<GrantedAuthority> authorityList = AuthorityUtils.createAuthorityList(userRoles.toArray(a));securityUser.setAuthorities(authorityList);return securityUser;}return null;}}

UserDetails这个也是个接口,其中定义了几种方法,都是围绕着用户名、密码、权限+角色集合这三个属性,因此我们可以实现这个类拓展这些字段,SecurityUser代码如下:

@Data
public class SecurityUser implements UserDetails {//用户名private String username;//密码private String password;//权限private Collection<? extends GrantedAuthority> authorities;public SecurityUser(String username, String password, Collection<? extends GrantedAuthority> authorities) {this.username = username;this.password = password;this.authorities = authorities;}public SecurityUser(){}@Overridepublic Collection<? extends GrantedAuthority> getAuthorities() {return authorities;}@Overridepublic String getPassword() {return password;}@Overridepublic String getUsername() {return username;}// 账户是否未过期@Overridepublic boolean isAccountNonExpired() {return true;}// 账户是否未被锁@Overridepublic boolean isAccountNonLocked() {return true;}@Overridepublic boolean isCredentialsNonExpired() {return true;}@Overridepublic boolean isEnabled() {return true;}
}

Token校验过滤器

客户端请求头携带了token,服务端肯定是需要针对每次请求解析、校验token,因此必须定义一个Token过滤器,这个过滤器的主要逻辑如下:

  • 从请求头中获取accessToken

  • 对accessToken解析、验签、校验过期时间

  • 校验成功,将authentication存入ThreadLocal中,这样方便后续直接获取用户详细信息。

@Component
public class TokenAuthenticationFilter extends OncePerRequestFilter {/*** JWT的工具类*/@Autowiredprivate JwtUtil jwtUtil;/*** UserDetailsService的实现类,从数据库中加载用户详细信息*/@Qualifier("jwtTokenUserDetailsService")@Autowiredprivate UserDetailsService userDetailsService;@AutowiredRedisTemplate redisTemplate;@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException {String token = request.getHeader("token");/*** token存在则校验token* 1. token是否存在* 2. token存在:*  2.1 校验token中的用户名是否失效*/if (!StringUtils.isEmpty(token)){DecodedJWT decodedJWT = jwtUtil.getTokenInfo(token);String username;try {username = decodedJWT.getClaim("username").asString();}catch (Exception e){throw new RuntimeException("token无效");}//从redis缓存中获得对应用户数据RedisUser redisUser = (RedisUser) redisTemplate.opsForValue().get(username);String[] a={};List<GrantedAuthority> authorityList = AuthorityUtils.createAuthorityList(redisUser.getAuthorities().toArray(a));UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(redisUser, null,authorityList);authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));// 将 authentication 存入 ThreadLocal,方便后续获取用户信息SecurityContextHolder.getContext().setAuthentication(authentication);}//继续执行下一个过滤器chain.doFilter(request,response);}
}

登录认证过滤器接口配置

上述定义了一个认证过滤器JwtAuthenticationLoginFilter,这个是用来登录的过滤器,但是并没有注入加入Spring Security的过滤器链中,需要定义配置,代码如下:

@Configuration
public class JwtAuthenticationSecurityConfig extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {/*** userDetailService*/@Qualifier("jwtTokenUserDetailsService")@Autowiredprivate UserDetailsService userDetailsService;/*** 登录成功处理器*/@Autowiredprivate LoginAuthenticationSuccessHandler loginAuthenticationSuccessHandler;/*** 登录失败处理器*/@Autowiredprivate LoginAuthenticationFailureHandler loginAuthenticationFailureHandler;/*** 加密*/@Autowiredprivate PasswordEncoder passwordEncoder;/*** 将登录接口的过滤器配置到过滤器链中* 1. 配置登录成功、失败处理器* 2. 配置自定义的userDetailService(从数据库中获取用户数据)* 3. 将自定义的过滤器配置到spring security的过滤器链中,配置在UsernamePasswordAuthenticationFilter之前* @param*/@Overridepublic void configure(HttpSecurity http) {JwtAuthenticationLoginFilter filter = new JwtAuthenticationLoginFilter();filter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class));//认证成功处理器filter.setAuthenticationSuccessHandler(loginAuthenticationSuccessHandler);//认证失败处理器filter.setAuthenticationFailureHandler(loginAuthenticationFailureHandler);//直接使用DaoAuthenticationProviderDaoAuthenticationProvider provider = new DaoAuthenticationProvider();//设置userDetailServiceprovider.setUserDetailsService(userDetailsService);//设置加密算法provider.setPasswordEncoder(passwordEncoder);http.authenticationProvider(provider);//将这个过滤器添加到UsernamePasswordAuthenticationFilter之前执行http.addFilterBefore(filter, UsernamePasswordAuthenticationFilter.class);}
}

Spring Security全局配置

上述仅仅配置了登录过滤器,还需要在全局配置类做一些配置,如下:

  • 应用登录过滤器的配置

  • 将登录接口、令牌刷新接口放行,不需要拦截

  • 配置AuthenticationEntryPoint、AccessDeniedHandler

  • 禁用session,前后端分离+JWT方式不需要session

  • 将token校验过滤器TokenAuthenticationFilter添加到过滤器链中,放在UsernamePasswordAuthenticationFilter之前。

@Configuration
//@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {@Autowiredprivate JwtAuthenticationSecurityConfig jwtAuthenticationSecurityConfig;@Autowiredprivate EntryPointUnauthorizedHandler entryPointUnauthorizedHandler;@Autowiredprivate RequestAccessDeniedHandler requestAccessDeniedHandler;@Overrideprotected void configure(HttpSecurity http) throws Exception {http.formLogin()//禁用表单登录,前后端分离用不上.disable()//应用登录过滤器的配置,配置分离.apply(jwtAuthenticationSecurityConfig).and()// 设置URL的授权.authorizeRequests().antMatchers("/login").permitAll()// anyRequest() 所有请求   authenticated() 必须被认证.anyRequest().authenticated()//处理异常情况:认证失败和权限不足.and().exceptionHandling()//认证未通过,不允许访问异常处理器.authenticationEntryPoint(entryPointUnauthorizedHandler)//认证通过,但是没权限处理器.accessDeniedHandler(requestAccessDeniedHandler).and()//禁用session,JWT校验不需要session.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and()//将TOKEN校验过滤器配置到过滤器链中,否则不生效,放到UsernamePasswordAuthenticationFilter之前.addFilterBefore(authenticationTokenFilterBean(), UsernamePasswordAuthenticationFilter.class)// 关闭csrf.csrf().disable();}// 自定义的Jwt Token校验过滤器@Beanpublic TokenAuthenticationFilter authenticationTokenFilterBean() {return new TokenAuthenticationFilter();}/*** 加密算法** @return*/@Bean@Overrideprotected AuthenticationManager authenticationManager() throws Exception {return super.authenticationManager();}
}

util包

JWT工具类

@Component
@ConfigurationProperties(prefix = "jwt")
//@Data
public class JwtUtil {private  String signature="cbac";private  Integer expiration=12;/**** 生成token header.payload.signature*/public  String getToken(Map<String,String> payload){Calendar calendar = Calendar.getInstance();calendar.add(Calendar.HOUR, 24);  // 24小时JWTCreator.Builder builder = JWT.create();// 构建payloadpayload.forEach(builder::withClaim);// 指定签发时间、过期时间 和 签名算法,并返回tokenString token = builder.withIssuedAt(new Date()).withExpiresAt(calendar.getTime()).sign(Algorithm.HMAC256(signature));return token;}/**** 获取token信息*/public  DecodedJWT getTokenInfo(String token){DecodedJWT verify=JWT.require(Algorithm.HMAC256(signature)).build().verify(token);return verify;}}

结果封装类

public class ResponseUtils {public static void result(HttpServletResponse response, ResultMsg msg) throws IOException {response.setContentType("application/json;charset=UTF-8");ServletOutputStream out = response.getOutputStream();ObjectMapper objectMapper = new ObjectMapper();out.write(objectMapper.writeValueAsString(msg).getBytes("UTF-8"));out.flush();out.close();}
}

测试结果

项目目录结构

 

http://www.bjxfkj.com.cn/article/104664.html

相关文章:

  • 做网站备案时间深圳创新创业大赛
  • 专业网页制作软件能帮助客户组织和管理网站seo优化怎么做
  • 平顶山市做网站国外免费网站建设
  • 自己做的网站放在服务器哪里邯郸seo推广
  • 打好代码怎么做网站seo顾问服务四川
  • 自己的电脑做服务区 网站典型的网络营销案例
  • 博客系统做网站长沙网站优化指导
  • wordpress 重置专业seo外包
  • 义乌网站建设公司seo推广优化外包公司
  • 开通网站后网络营销是什么工作主要干啥
  • 做竞价网站访问突然变少网络营销培训课程
  • 南通医院网站建设方案网络广告的收费模式有哪些
  • 淘宝网站都是怎么做的成都百度推广电话
  • 网站用空间还是服务器推广宣传文案
  • 免费模板app下载推推蛙贴吧优化
  • 网站海外推广建设网站关键词怎么设置
  • 商标查询官方网站深圳网
  • 快速做网站公司报价搜索量排行
  • 昆明做网站建设的公司排名网站制作公司排行榜
  • dedecms网站版权信息淘宝店铺如何推广
  • 网站建设公司是什么代运营公司哪家好一些
  • 河南郑州网络科技有限公司汕头seo推广
  • 佛山网站建设价格品牌营销经典案例
  • 小荷特卖的网站谁做的2020新闻大事件摘抄
  • 钟楼做网站广东知名seo推广多少钱
  • 怎么与其他网站做友情链接快优吧seo优化
  • windows搭建网站开发百度一下百度搜索首页
  • 速拓科技是做网站北京百度总部
  • 网站建设课程教学改革搜索引擎优化培训中心
  • 营销型网站建设实训报告个人总结代推广平台