springboot+springsecurity+JWT
2020-05-07 16:03:05来源:博客园 阅读 ()
springboot+springsecurity+JWT
目录结构:
springbootApplication里面的配置,如有需要可以自行添加
实现JWT功能
在pom.xml中的配置
1 <?xml version="1.0" encoding="UTF-8"?> 2 <project xmlns="http://maven.apache.org/POM/4.0.0" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> 5 <modelVersion>4.0.0</modelVersion> 6 7 <parent> 8 <groupId>org.springframework.boot</groupId> 9 <artifactId>spring-boot-starter-parent</artifactId> 10 <version>2.1.5.RELEASE</version> 11 </parent> 12 13 <groupId>com.my</groupId> 14 <artifactId>my-springsecurity</artifactId> 15 <version>1.0-SNAPSHOT</version> 16 17 18 <dependencies> 19 <dependency> 20 <groupId>org.springframework.boot</groupId> 21 <artifactId>spring-boot-starter-web</artifactId> 22 </dependency> 23 <dependency> 24 <groupId>org.springframework.boot</groupId> 25 <artifactId>spring-boot-starter-thymeleaf</artifactId> 26 </dependency> 27 <dependency> 28 <groupId>org.springframework.boot</groupId> 29 <artifactId>spring-boot-starter-jdbc</artifactId> 30 </dependency> 31 <dependency> 32 <groupId>org.springframework.data</groupId> 33 <artifactId>spring-data-redis</artifactId> 34 <version>2.1.5.RELEASE</version> 35 </dependency> 36 <dependency> 37 <groupId>redis.clients</groupId> 38 <artifactId>jedis</artifactId> 39 </dependency> 40 <dependency> 41 <groupId>org.springframework.boot</groupId> 42 <artifactId>spring-boot-test</artifactId> 43 <scope>test</scope> 44 </dependency> 45 <!-- oracle数据库驱动 --> 46 <dependency> 47 <groupId>com.oracle</groupId> 48 <artifactId>ojdbc6</artifactId> 49 <version>11.2.0.3</version> 50 </dependency> 51 <dependency> 52 <groupId>org.mybatis.spring.boot</groupId> 53 <artifactId>mybatis-spring-boot-starter</artifactId> 54 <version>1.3.2</version> 55 </dependency> 56 57 <dependency> 58 <groupId>org.springframework.boot</groupId> 59 <artifactId>spring-boot-starter-security</artifactId> 60 </dependency> 61 <dependency> 62 <groupId>org.springframework.security</groupId> 63 <artifactId>spring-security-web</artifactId> 64 <version>5.0.4.RELEASE</version> 65 </dependency> 66 <dependency> 67 <groupId>org.springframework.security</groupId> 68 <artifactId>spring-security-config</artifactId> 69 <version>5.0.4.RELEASE</version> 70 </dependency> 71 <dependency> 72 <groupId>io.jsonwebtoken</groupId> 73 <artifactId>jjwt</artifactId> 74 <version>0.9.0</version> 75 </dependency> 76 77 <dependency> 78 <groupId>com.alibaba</groupId> 79 <artifactId>fastjson</artifactId> 80 <version>1.2.30</version> 81 </dependency> 82 <dependency> 83 <groupId>org.springframework.boot</groupId> 84 <artifactId>spring-boot-starter-test</artifactId> 85 <scope>test</scope> 86 </dependency> 87 </dependencies> 88 89 <build> 90 <finalName>${project.artifactId}</finalName> 91 <plugins> 92 <!-- <!– 资源文件拷贝插件 –> 93 <plugin> 94 <groupId>org.apache.maven.plugins</groupId> 95 <artifactId>maven-resources-plugin</artifactId> 96 <configuration> 97 <encoding>UTF-8</encoding> 98 </configuration> 99 </plugin>--> 100 <!-- java编译插件 --> 101 <plugin> 102 <groupId>org.apache.maven.plugins</groupId> 103 <artifactId>maven-compiler-plugin</artifactId> 104 <configuration> 105 <source>1.8</source> 106 <target>1.8</target> 107 <encoding>UTF-8</encoding> 108 </configuration> 109 </plugin> 110 <plugin> 111 <groupId>org.springframework.boot</groupId> 112 <artifactId>spring-boot-maven-plugin</artifactId> 113 </plugin> 114 </plugins> 115 </build> 116 </project>maven
在application.yml中的配置
1 server: 2 port: ${PORT:8080} 3 spring: 4 application: 5 name: my-springsecurity 6 datasource: 7 url: jdbc:oracle:thin:@localhost:1521:orcl 8 username: scott 9 password: oracle 10 driver-class-name: oracle.jdbc.driver.OracleDriver 11 redis: 12 host: ${REDIS_HOST:127.0.0.1} 13 port: ${REDIS_PORT:6379} 14 timeout: 5000 #连接超时 毫秒 15 # main: 16 # allow-bean-definition-overriding: true #同名bean覆盖 17 security: 18 loginType: JSONs 19 20 # 无法扫描到resources下的templates中的静态文件时可以配置,如果可以无需配置 21 #注入到TomcatConfig 22 bw: 23 factory: 24 doc: 25 root: E:\JAVAIDEA\My\springboot\my-springsecurity\src\main\resources\templatesapplication.yml
首先需要继承WebSecurityConfigurerAdapter类,配置security中的认证授权相关配置
1 @EnableWebSecurity 2 @EnableGlobalMethodSecurity(prePostEnabled = true) 3 public class SecurityConfig extends WebSecurityConfigurerAdapter { 4 5 //token 过滤器,解析token 6 @Autowired 7 MyJwtTokenFilter jwtTokenFilter; 8 9 10 @Autowired 11 MyUserDetailsService myUserDetailsService; 12 13 @Autowired 14 private SendSmsSecurityConfig sendSmsSecurityConfig; 15 16 //加密机制 17 @Bean 18 public PasswordEncoder passwordEncoder() { 19 return NoOpPasswordEncoder.getInstance(); 20 } 21 22 23 //认证 24 @Override 25 protected void configure(AuthenticationManagerBuilder auth) throws Exception { 26 auth.userDetailsService(myUserDetailsService) 27 .passwordEncoder(passwordEncoder()); 28 } 29 30 @Override 31 protected void configure(HttpSecurity http) throws Exception { 32 http.authorizeRequests() 33 // .antMatchers("/admin/api/**").hasRole("ADMIN") 34 // .antMatchers("/user/api/**").hasRole("USER") 35 .antMatchers("/SendSms").permitAll() 36 .antMatchers("/**").permitAll() 37 .anyRequest().authenticated()//任何请求登录后访问 38 .and() 39 // 基于token,所以不需要session 40 .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) 41 // .sessionManagement() //会话管理 42 // .maximumSessions(1); //限制登录人数 43 .and().csrf().disable(); 44 45 //登录操作 46 http.formLogin() 47 .loginPage("/login.html")//登录路径 48 .loginProcessingUrl("/farmerlogin")//登录表单提交请求 49 .usernameParameter("username")//设置登录账号参数,默认username 50 .passwordParameter("password")//设置登录密码参数,默认password 51 // .defaultSuccessUrl("/home.html")//登录成功跳转,不能和successHandler一起使用 52 // .failureUrl("/login.html")// 登录失败跳转,不能和failureHandler一起使用 53 .permitAll() 54 //登录成功处理 55 .successHandler(new LoginSuccessHandler()) 56 //登录失败处理 57 .failureHandler(new LoginFailureHandler()) 58 .and().apply(sendSmsSecurityConfig); 59 60 61 //退出操作 62 http.logout() 63 .logoutUrl("/aaa")//退出提交参数 64 .logoutSuccessUrl("/login.html");//退出后跳转,不能和logoutSuccessHandler一起使用 65 // .logoutSuccessHandler();//退出处理 66 67 // 禁用缓存 68 http.headers().cacheControl(); 69 70 http.addFilterBefore(jwtTokenFilter, UsernamePasswordAuthenticationFilter.class) 71 // 添加权限不足 filter 72 .exceptionHandling().accessDeniedHandler(new MyAccessDeniedHandler()) 73 //其他异常处理类 74 .authenticationEntryPoint(new MyAuthenticationException()); 75 } 76 77 //忽略拦截 78 @Override 79 public void configure(WebSecurity web) throws Exception { 80 web.ignoring().antMatchers("/css/**", "/elementuidemo/**", 81 "/img/**", "/js/**", "/plugins/**", "/static/json/**", "/pages/**"); 82 } 83 84 }WebSecurityConfigurerAdapter的配置类
配置相关配件:
登录成功处理:继承SavedRequestAwareAuthenticationSuccessHandler类,该类继承AuthenticationSuccessHandler类,AuthenticationSuccessHandler登录成功父类,之所以用SavedRequestAwareAuthenticationSuccessHandler类,里面可以实现其他的方法,如:登录成功跳转到登录之前请求的页面
1 /** 2 * <p> 3 * AuthenticationSuccessHandler登录成功父类 4 * SavedRequestAwareAuthenticationSuccessHandler 继承AuthenticationSuccessHandler 5 * <p> 6 * 登录成功处理 7 */ 8 @Component 9 public class LoginSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler { 10 11 /* @Value("${spring.security.loginType}") 12 private String loginType;*/ 13 14 /* @Autowired 15 JwtTokenUtil jwtTokenUtil;*/ 16 17 @Override 18 public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException { 19 System.out.println("登录成功"); 20 //从authentication中获取用户信息 21 final User farmerDetail = (User) authentication.getPrincipal(); 22 JwtTokenUtil jwtTokenUtil = new JwtTokenUtil(); 23 //生成jwt 24 String token = jwtTokenUtil.generateToken(farmerDetail); 25 httpServletResponse.addHeader("token", "Bearer " + token); 26 27 /* if (loginType.equalsIgnoreCase("JSON")) { 28 httpServletResponse.setContentType("application/json"); 29 httpServletResponse.setCharacterEncoding("UTF-8"); 30 httpServletResponse.getWriter().write("{\"result\":\"ok\"}"); 31 // httpServletResponse.getWriter().flush(); 32 } else { 33 //跳转到登录之前请求的页面 34 super.onAuthenticationSuccess(httpServletRequest, httpServletResponse, authentication); 35 }*/ 36 httpServletResponse.setContentType("application/json"); 37 httpServletResponse.setCharacterEncoding("UTF-8"); 38 httpServletResponse.getWriter().write("{\"result\":\"ok\"}"); 39 40 } 41 }LoginSuccessHandler类
登录失败处理:继承SimpleUrlAuthenticationFailureHandler类,该类继承SimpleUrlAuthenticationFailureHandler类,SimpleUrlAuthenticationFailureHandler类登录失败父类,之所以用SimpleUrlAuthenticationFailureHandler类,里面可以实现其他方法,如:登录失败跳转到登录页面
1 /** 2 * 3 * AuthenticationFailureHandler登录失败父类 4 * SimpleUrlAuthenticationFailureHandler 继承AuthenticationFailureHandler 5 * 6 * 登录失败处理 7 */ 8 @Component 9 public class LoginFailureHandler extends SimpleUrlAuthenticationFailureHandler { 10 11 /* @Value("${spring.security.loginType}") 12 private String loginType;*/ 13 14 @Override 15 public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException { 16 /* if (loginType.equalsIgnoreCase("JSON")) { 17 httpServletResponse.setContentType("application/json;charset=UTF-8"); 18 httpServletResponse.setStatus(401); 19 PrintWriter out = httpServletResponse.getWriter(); 20 out.write("{\"error_code\":\"401\",\"name\":\""+e.getClass()+"\",\"message\":\""+e.getMessage()+"\"}"); 21 22 }else { 23 //登录失败跳转到登录页面 24 super.onAuthenticationFailure(httpServletRequest,httpServletResponse,e); 25 }*/ 26 27 httpServletResponse.setContentType("application/json;charset=UTF-8"); 28 httpServletResponse.setStatus(401); 29 PrintWriter out = httpServletResponse.getWriter(); 30 out.write("{\"error_code\":\"401\",\"name\":\""+e.getClass()+"\",\"message\":\""+e.getMessage()+"\"}"); 31 32 } 33 }LoginFailureHandler类
权限不足处理:实现的AccessDeniedHandler类,进行的权限校验
1 /** 2 * Spring security权限不足处理类 3 * 只有登录后(即接口有传token)接口权限不足才会进入AccessDeniedHandler, 4 * 如果是未登陆或者会话超时等,不会触发AccessDeniedHandler,而是会直接跳转到登陆页面。 5 */ 6 @Component 7 public class MyAccessDeniedHandler implements AccessDeniedHandler { 8 @Override 9 public void handle(HttpServletRequest httpServletRequest, HttpServletResponse response, AccessDeniedException e) throws IOException, ServletException { 10 //登陆状态下,权限不足执行该方法 11 System.out.println("权限不足:" + e.getMessage()); 12 response.setStatus(200); 13 response.setCharacterEncoding("UTF-8"); 14 response.setContentType("application/json; charset=utf-8"); 15 PrintWriter printWriter = response.getWriter(); 16 response.getWriter().write("{\"result\":\"权限不足\"}"); 17 printWriter.flush(); 18 } 19 }MyAccessDeniedHandler类
异常处理:实现AuthenticationEntryPoint类,实现异常的处理,如果不配置此类,则spring security默认会跳转到登录页面
1 /** 2 * Spring security其他异常处理类,比如请求路径不存在等, 3 * 如果不配置此类,则Spring security默认会跳转到登录页面 4 */ 5 @Component 6 public class MyAuthenticationException implements AuthenticationEntryPoint { 7 @Override 8 public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException { 9 System.out.println("AuthenticationEntryPoint检测到异常:"+e); 10 httpServletResponse.setStatus(200); 11 httpServletResponse.setCharacterEncoding("UTF-8"); 12 httpServletResponse.setContentType("application/json; charset=utf-8"); 13 PrintWriter printWriter = httpServletResponse.getWriter(); 14 httpServletResponse.getWriter().write("AuthenticationEntryPoint检测到异常:"+e); 15 printWriter.flush(); 16 } 17 }MyAuthenticationException类
UserDetailsService验证用户名、密码和授权处理,实现从数据库查询用户功能
1 /** 2 * 根据账号查询用户 3 */ 4 @Component 5 public class MyUserDetailsService implements UserDetailsService { 6 7 @Autowired 8 private UserMapper userMapper; 9 10 11 12 @Override 13 public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { 14 15 //从数据库读取该用户 16 User user = userMapper.findByUserName(username); 17 // 用户不存在,抛出异常 18 if (user == null){ 19 throw new UsernameNotFoundException("用户不存在"); 20 } 21 //将数据库形式的roles解析为UserDtails的权限集 22 // farmer.setAuthorities(AuthorityUtils.commaSeparatedStringToAuthorityList(farmer.getRoles())); 23 List<GrantedAuthority> authorities = generateAuthorities(user.getRoles()); 24 user.setAuthorities(authorities); 25 return user; 26 27 //基于内存验证用户信息 28 // UserDetails userDetails = Farmer.withUsername("123").password("123").roles("USER").build(); 29 // return userDetails; 30 } 31 32 //自定义实现权限转换 33 private List<GrantedAuthority> generateAuthorities(String roles){ 34 List<GrantedAuthority> authorities = new ArrayList<>(); 35 String[] roleArray = roles.split(","); 36 if (roles != null && !"".equals(roles)){ 37 for (String role : roleArray) { 38 authorities.add(new SimpleGrantedAuthority(role)); 39 } 40 } 41 return authorities; 42 } 43 }MyUserDetailsService类
token过滤器进行解析判断是否登录,继承OncePerRequestFilter类
1 /** 2 * token 过滤器,在这里解析token,拿到该用户角色,设置到springsecurity的上下文环境中,让springsecurity自动判断权限 3 * 所有请求最先进入此过滤器,包括登录接口,而且在springsecurity的密码验证之前执行 4 */ 5 @Component 6 public class MyJwtTokenFilter extends OncePerRequestFilter { 7 8 @Autowired 9 MyUserDetailsService myUserDetailsService; 10 @Autowired 11 private JwtTokenUtil jwtTokenUtil; 12 13 14 @Override 15 protected void doFilterInternal(HttpServletRequest request, 16 HttpServletResponse response, 17 FilterChain chain) 18 throws ServletException, IOException { 19 20 String authHeader = request.getHeader("Authorization"); 21 String tokenHead = "Bearer "; 22 23 if (authHeader != null && authHeader.startsWith(tokenHead)) { 24 String authToken = authHeader.substring(tokenHead.length()); 25 String username = jwtTokenUtil.getUsernameFromToken(authToken); 26 if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) { 27 UserDetails userDetails = this.myUserDetailsService.loadUserByUsername(username); 28 //验证令牌是否有效 29 if (jwtTokenUtil.validateToken(authToken, userDetails)) { 30 UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities()); 31 authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); 32 SecurityContextHolder.getContext().setAuthentication(authentication); 33 } 34 } 35 } 36 chain.doFilter(request, response); 37 } 38 }MyJwtTokenFilter类
JWT工具类
1 @Component 2 public class JwtTokenUtil implements Serializable { 3 /** 4 * 密钥 5 */ 6 private final String secret = "aaaaaaaa"; 7 8 /** 9 * 从数据声明生成令牌 10 * 11 * @param claims 数据声明 12 * @return 令牌 13 */ 14 private String generateToken(Map<String, Object> claims) { 15 Date expirationDate = new Date(System.currentTimeMillis() + 2592000L * 1000); 16 return Jwts.builder().setClaims(claims).setExpiration(expirationDate).signWith(SignatureAlgorithm.HS512, secret).compact(); 17 } 18 19 /** 20 * 从令牌中获取数据声明 21 * 22 * @param token 令牌 23 * @return 数据声明 24 */ 25 private Claims getClaimsFromToken(String token) { 26 Claims claims; 27 try { 28 claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody(); 29 } catch (Exception e) { 30 claims = null; 31 } 32 return claims; 33 } 34 35 /** 36 * 生成令牌 37 * 38 * @param userDetails 用户 39 * @return 令牌 40 */ 41 public String generateToken(UserDetails userDetails) { 42 Map<String, Object> claims = new HashMap<>(2); 43 claims.put("sub", userDetails.getUsername()); 44 claims.put("created", new Date()); 45 return generateToken(claims); 46 } 47 48 /** 49 * 从令牌中获取用户名 50 * 51 * @param token 令牌 52 * @return 用户名 53 */ 54 public String getUsernameFromToken(String token) { 55 String username; 56 try { 57 Claims claims = getClaimsFromToken(token); 58 username = claims.getSubject(); 59 } catch (Exception e) { 60 username = null; 61 } 62 return username; 63 } 64 65 /** 66 * 判断令牌是否过期 67 * 68 * @param token 令牌 69 * @return 是否过期 70 */ 71 public Boolean isTokenExpired(String token) { 72 try { 73 Claims claims = getClaimsFromToken(token); 74 Date expiration = claims.getExpiration(); 75 return expiration.before(new Date()); 76 } catch (Exception e) { 77 return false; 78 } 79 } 80 81 /** 82 * 刷新令牌 83 * 84 * @param token 原令牌 85 * @return 新令牌 86 */ 87 public String refreshToken(String token) { 88 String refreshedToken; 89 try { 90 Claims claims = getClaimsFromToken(token); 91 claims.put("created", new Date()); 92 refreshedToken = generateToken(claims); 93 } catch (Exception e) { 94 refreshedToken = null; 95 } 96 return refreshedToken; 97 } 98 99 /** 100 * 验证令牌 101 * 102 * @param token 令牌 103 * @param userDetails 用户 104 * @return 是否有效 105 */ 106 public Boolean validateToken(String token, UserDetails userDetails) { 107 User user = (User) userDetails; 108 String username = getUsernameFromToken(token); 109 return (username.equals(user.getUsername()) && !isTokenExpired(token)); 110 } 111 }JwtTokenUtil类
在pojo文件中,写User实体类实现UserDetails类
1 public class User implements UserDetails{ 2 3 private int id; 4 private String username; 5 private String password; 6 private String roles; 7 8 9 private List<GrantedAuthority> authorities; 10 11 public int getId() { 12 return id; 13 } 14 15 public void setId(int id) { 16 this.id = id; 17 } 18 19 public void setUsername(String username) { 20 this.username = username; 21 } 22 23 public void setPassword(String password) { 24 this.password = password; 25 } 26 @Override 27 public String getPassword() { 28 return password; 29 } 30 31 @Override 32 public String getUsername() { 33 return username; 34 } 35 36 public String getRoles() { 37 return roles; 38 } 39 40 public void setRoles(String roles) { 41 this.roles = roles; 42 } 43 44 public void setAuthorities(List<GrantedAuthority> authorities) { 45 this.authorities = authorities; 46 } 47 48 @Override 49 public Collection<? extends GrantedAuthority> getAuthorities() { 50 return this.authorities; 51 } 52 53 54 //账号是否没过期 55 @Override 56 public boolean isAccountNonExpired() { 57 return true; 58 } 59 //账号是否没被锁定 60 @Override 61 public boolean isAccountNonLocked() { 62 return true; 63 } 64 //密码是否没过期 65 @Override 66 public boolean isCredentialsNonExpired() { 67 return true; 68 } 69 //账号是否可用 70 @Override 71 public boolean isEnabled() { 72 return true; 73 } 74 75 @Override 76 public String toString() { 77 return "User{" + 78 "id=" + id + 79 ", username='" + username + '\'' + 80 ", password='" + password + '\'' + 81 ", roles='" + roles + '\'' + 82 ", authorities=" + authorities + 83 '}'; 84 } 85 }User类
dao层,mapper.xml,数据库可以进行简单的配置进行测试。数据库为Oracle,可以自行配置mysql或Oracle
数据库就四个字段,id,username,password,权限
controller中有三个测试类
1 @RestController 2 @RequestMapping("/admin/api") 3 public class AdminController { 4 5 @GetMapping("hello") 6 @PreAuthorize("hasRole('ROLE_ADMIN')") 7 public String hello(){ 8 return "hello,admin"; 9 } 10 }AdminController
1 @RestController 2 @RequestMapping("/app/api") 3 public class AppController { 4 5 @GetMapping("hello") 6 public String hello(){ 7 return "hello,app"; 8 } 9 }AppController
1 @RestController 2 @RequestMapping("/user/api") 3 public class UserController { 4 5 @GetMapping("/hello") 6 @PreAuthorize("hasAuthority('ROLE_USER')") 7 public String hello(){ 8 return "hello,user"; 9 } 10 }UserController
在Postman中进行登录测试
登录成功显示: farmerlogin是在SecurityConfig中自定义的,默认为login
会在Headers中生成一个token
这个token是自己定义的,在登录成功处理类中生成的token令牌
在验证时,在Key中存放 Authorization ,在Value中把生成的token保存进去,然后会在MyJwtTokenFilter类中进行令牌校验
成功示例:
SendSmsSecurityConfig类为短信登录的配置类,须在SecurityConfig进行声明
SendSms文件中的类为短信登录相关的配置,和本章并无冲突,遇到相关配置可直接删除
不重要需要了可以配置结合:
1 @Configuration 2 public class TomcatConfig { 3 @Value("${bw.factory.doc.root}") 4 private String rootDoc; 5 @Bean 6 public AbstractServletWebServerFactory embeddedServletContainerFactory() { 7 8 TomcatServletWebServerFactory tomcatServletWebServerFactory = new TomcatServletWebServerFactory(); 9 tomcatServletWebServerFactory.setDocumentRoot( 10 new File(rootDoc)); 11 return tomcatServletWebServerFactory; 12 } 13 }TomcatConfig
原文链接:https://www.cnblogs.com/foshuo-cv/p/12842075.html
如有疑问请与原作者联系
标签:
版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有
IDC资讯: 主机资讯 注册资讯 托管资讯 vps资讯 网站建设
网站运营: 建站经验 策划盈利 搜索优化 网站推广 免费资源
网络编程: Asp.Net编程 Asp编程 Php编程 Xml编程 Access Mssql Mysql 其它
服务器技术: Web服务器 Ftp服务器 Mail服务器 Dns服务器 安全防护
软件技巧: 其它软件 Word Excel Powerpoint Ghost Vista QQ空间 QQ FlashGet 迅雷
网页制作: FrontPages Dreamweaver Javascript css photoshop fireworks Flash