ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

spring boot security 安全权限 ---2 短信/图片验证码 认证授权

2022-01-02 14:34:07  阅读:139  来源: 互联网

标签:spring boot springframework --- import org security com public


二、spring security 短信验证 认证授权

A、自定义图片验证码验证

         1、基于spring security重写图片验证码验证的过滤器ImgCodeFilter

package com.example.springsecurity.filter;

import com.example.springsecurity.exception.ImgException;
import com.example.springsecurity.handler.MyAuthenticationFailureHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @Author: LW
 * @Date: 2021/12/26 10:24
 * @Title: 数字验证过滤器,可用在图片验证码验证
 */
@Component
public class ImgCodeFilter extends OncePerRequestFilter {
    @Autowired
    MyAuthenticationFailureHandler authenticationFailureHandler;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        //从请求中获取请求地址和方式进行判断是否是登录请求验证图片验证码
        if("/login".equals(request.getRequestURI())&&"post".equalsIgnoreCase(request.getMethod())){
            try{
                verityCode(request);
            }catch (ImgException e){
                authenticationFailureHandler.onAuthenticationFailure(request,response,e);
            }
        }
        doFilter(request,response,filterChain);
    }

    //验证图片验证码
    public void verityCode(HttpServletRequest request) throws ImgException {
        //图片验证码的在页面显示需要调用生成图片验证码的工具类,验证码生成后会先存入redis,在此略
        //这里的1234是自定义的,在实际开发中是从redis获取
        if(!"1234".equals(request.getParameter("code"))){
            throw new ImgException("验证码错误");
        }
    }
}

  2、重写登录异常

package com.example.springsecurity.exception;

import org.springframework.security.core.AuthenticationException;


public class ImgException extends AuthenticationException {

    public ImgException(String explanation) {
        super(explanation);
    }
}

3、SecurityConfig中注入过滤器,并把过滤器加入security

@Autowired
private ImgCodeFilter imgCodeFilter;
 //验证用户名密码之前进行过滤验证
 http.addFilterBefore(imgCodeFilter, UsernamePasswordAuthenticationFilter.class);

4、修改登录失败异常处理

if(e instanceof LockedException){
            map.put("msg","账户被锁定,无法登录");
        }else if(e instanceof BadCredentialsException){
            map.put("msg","用户名或密码错误");
        }else if(e instanceof DisabledException){
            map.put("msg","账户被禁用,无法登录");
        }else if(e instanceof AccountExpiredException){
            map.put("msg","账户已过期,无法登录");
        }else if(e instanceof CredentialsExpiredException){
            map.put("msg","密码已过期,无法登录");
        }else if(e instanceof ImgException){
            map.put("msg",e.getMessage());
        }else{
            map.put("msg","登录异常,请联系管理员");
        }

5、登录html加入图形验证码

B、短信验证码登录开发

   

1、重写AbstractAuthenticationToken中的方法

package com.example.springsecurity.handler;

import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;

import java.util.Collection;

/**
 * @Author: yushizhong
 * @Date: 2020/1/9 14:38
 * @Title: 短信验证码token,对应UsernamePasswordAuthenticationToken
 */
public class SmsCodeAuthenticationToken extends AbstractAuthenticationToken {
    private static final long serialVersionUID = 500L;
    private final Object principal;

    public SmsCodeAuthenticationToken(Object mobile) {
        super((Collection)null);
        this.principal = mobile;
        this.setAuthenticated(false);
    }

    public SmsCodeAuthenticationToken(Object principal, Collection<? extends GrantedAuthority> authorities) {
        super(authorities);
        this.principal = principal;
        super.setAuthenticated(true);
    }

    public Object getCredentials() {
        return null;
    }

    public Object getPrincipal() {
        return this.principal;
    }

    public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
        if (isAuthenticated) {
            throw new IllegalArgumentException("Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead");
        } else {
            super.setAuthenticated(false);
        }
    }

    public void eraseCredentials() {
        super.eraseCredentials();
    }

}

2、重写AbstractAuthenticationProcessingFilter中的方法

package com.example.springsecurity.handler;

import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;

import java.util.Collection;

/**
 * @Author: yushizhong
 * @Date: 2020/1/9 14:38
 * @Title: 短信验证码token,对应UsernamePasswordAuthenticationToken
 */
public class SmsCodeAuthenticationToken extends AbstractAuthenticationToken {
    private static final long serialVersionUID = 500L;
    private final Object principal;

    public SmsCodeAuthenticationToken(Object mobile) {
        super((Collection)null);
        this.principal = mobile;
        this.setAuthenticated(false);
    }

    public SmsCodeAuthenticationToken(Object principal, Collection<? extends GrantedAuthority> authorities) {
        super(authorities);
        this.principal = principal;
        super.setAuthenticated(true);
    }

    public Object getCredentials() {
        return null;
    }

    public Object getPrincipal() {
        return this.principal;
    }

    public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
        if (isAuthenticated) {
            throw new IllegalArgumentException("Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead");
        } else {
            super.setAuthenticated(false);
        }
    }

    public void eraseCredentials() {
        super.eraseCredentials();
    }

}

3、重写AbstractAuthenticationProcessingFilter中的方法

package com.example.springsecurity.filter;

import com.example.springsecurity.handler.SmsCodeAuthenticationToken;
import org.springframework.lang.Nullable;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * @Author: yushizhong
 * @Date: 2020/1/9 14:40
 * @Title: 短信验证码认证过滤器,对应UsernamePasswordAuthenticationFilter
 */
public class SmsCodeAuthenticationFilter extends AbstractAuthenticationProcessingFilter  {

    public static final String SPRING_SECURITY_FORM_MOBILE_KEY = "mobile";
    private String mobileParameter = "mobile";
    private boolean postOnly = true;

    public SmsCodeAuthenticationFilter() {
        super(new AntPathRequestMatcher("/mobile", "POST"));
    }

    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {
        if (this.postOnly && !request.getMethod().equals("POST")) {
            throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
        } else {
            String mobile = this.obtainMobile(request);
            if (mobile == null) {
                mobile = "";
            }
            mobile = mobile.trim();
            SmsCodeAuthenticationToken authRequest = new SmsCodeAuthenticationToken(mobile);
            this.setDetails(request, authRequest);
            return this.getAuthenticationManager().authenticate(authRequest);
        }
    }

    @Nullable
    protected String obtainMobile(HttpServletRequest request) {
        return request.getParameter(this.mobileParameter);
    }

    protected void setDetails(HttpServletRequest request, SmsCodeAuthenticationToken authRequest) {
        authRequest.setDetails(this.authenticationDetailsSource.buildDetails(request));
    }

    public void setMobileParameter(String mobileParameter) {
        Assert.hasText(mobileParameter, "Username parameter must not be empty or null");
        this.mobileParameter = mobileParameter;
    }

    public void setPostOnly(boolean postOnly) {
        this.postOnly = postOnly;
    }

    public final String getMobileParameter() {
        return this.mobileParameter;
    }


}

4、重写AuthenticationProvider 中的方法

package com.example.springsecurity.handler;

import com.example.springsecurity.service.UserService;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.InternalAuthenticationServiceException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetails;

/**
 * @Author: yushizhong
 * @Date: 2020/1/9 14:43
 * @Title: 短信验证码认证校验器,对应DaoAuthenticationProvider
 */
public class SmsCodeAuthenticationProvider implements AuthenticationProvider {


    private UserService userService;

    public UserService getUserService() {
        return userService;
    }

    public void setUserService(UserService userService) {
        this.userService = userService;
    }

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        SmsCodeAuthenticationToken smsCodeAuthenticationToken = (SmsCodeAuthenticationToken)authentication;
        UserDetails user = userService.loadUserByUsername((String)smsCodeAuthenticationToken.getPrincipal());
        if (user == null) {
            throw new InternalAuthenticationServiceException("无法获取用户信息");
        }
        //构造认证结果
        SmsCodeAuthenticationToken result = new SmsCodeAuthenticationToken(user, user.getAuthorities());
        result.setDetails(smsCodeAuthenticationToken.getDetails());
        return result;
    }

    @Override
    public boolean supports(Class<?> authentication) {
        return SmsCodeAuthenticationToken.class.isAssignableFrom(authentication);
    }


}

5、短信验证码过滤器SmsCodeFilter,用于验证短信验证码是否正确

package com.example.springsecurity.filter;

import com.example.springsecurity.exception.SmsCodeException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;

/**
 * @Author: yushizhong
 * @Date: 2020/1/9 15:00
 * @Title: 用于验证短信验证码是否正确
 */
@Component
public class SmsCodeFilter extends OncePerRequestFilter implements InitializingBean {

    @Autowired
    private AuthenticationFailureHandler authenticationFailureHandler;

    private Set<String> urls = new HashSet<>();

    private AntPathMatcher antPathMatcher = new AntPathMatcher();

    @Override
    public void afterPropertiesSet() throws ServletException {
        super.afterPropertiesSet();
        // 这里配置需要拦截的地址
        urls.add("/mobile");

    }

    @Override
    protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) 
throws ServletException, IOException {
        boolean action = false;
        //判断请求地址
        for (String url : urls) {
            if (antPathMatcher.match(url, httpServletRequest.getRequestURI())) {
                action = true;
                break;
            }
        }
        if (action) {
            try {
                validate(httpServletRequest);
            } catch (SmsCodeException e) {
                authenticationFailureHandler.onAuthenticationFailure(httpServletRequest, httpServletResponse, e);
                return;
            }
        }
        filterChain.doFilter(httpServletRequest, httpServletResponse);
    }

    private void validate(HttpServletRequest request) {
       String code= (String) request.getSession().getAttribute("code");
        String smsCodeRequest = request.getParameter("smsCode");
        if (code == null) {
            throw new SmsCodeException("短信验证码不存在");
        }
        if(!smsCodeRequest.equalsIgnoreCase(code)) {
            throw new SmsCodeException("短信验证码错误");
        }
        //清除session
//        request.getSession().removeAttribute("code");
    }

}

6、SmsCodeAuthenticationSecurityConfig 短信验证登录处理中心

package com.example.springsecurity.config;

import com.example.springsecurity.filter.SmsCodeAuthenticationFilter;
import com.example.springsecurity.handler.MyAuthenticationFailureHandler;
import com.example.springsecurity.handler.MyAuthenticationSuccessHandler;
import com.example.springsecurity.handler.SmsCodeAuthenticationProvider;
import com.example.springsecurity.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.SecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.DefaultSecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.stereotype.Component;

/**
 * @Author: yushizhong
 * @Date: 2020/1/9 14:57
 * @Title: 短信验证码认证安全设置,重写configure方法
 */
@Component
public class SmsCodeAuthenticationSecurityConfig extends SecurityConfigurerAdapter<DefaultSecurityFilterChain, HttpSecurity> {
    @Autowired
    private MyAuthenticationFailureHandler myAuthenticationFailHandler;

    @Autowired
    private MyAuthenticationSuccessHandler myAuthenticationSuccessHandler;

    @Autowired
    private UserService userService;

    @Override
    public void configure(HttpSecurity http) throws Exception {

        SmsCodeAuthenticationFilter smsCodeAuthenticationFilter = new SmsCodeAuthenticationFilter();
        smsCodeAuthenticationFilter.setAuthenticationManager(http.getSharedObject(AuthenticationManager.class));
        smsCodeAuthenticationFilter.setAuthenticationSuccessHandler(myAuthenticationSuccessHandler);
        smsCodeAuthenticationFilter.setAuthenticationFailureHandler(myAuthenticationFailHandler);

        SmsCodeAuthenticationProvider smsCodeAuthenticationProvider = new SmsCodeAuthenticationProvider();
        smsCodeAuthenticationProvider.setUserService(userService);

        http.authenticationProvider(smsCodeAuthenticationProvider)
               .addFilterBefore(smsCodeAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); 
    }

}

7、SecurityConfig配置

package com.example.springsecurity.config;

import com.example.springsecurity.domain.Auth;
import com.example.springsecurity.filter.ImgCodeFilter;
import com.example.springsecurity.filter.SmsCodeFilter;
import com.example.springsecurity.handler.MyAuthenticationFailureHandler;
import com.example.springsecurity.handler.MyAuthenticationSuccessHandler;
import com.example.springsecurity.mapper.AuthMapper;
import com.example.springsecurity.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.annotation.web.configurers.ExpressionUrlAuthorizationConfigurer;
import org.springframework.security.core.Authentication;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.LogoutHandler;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.stereotype.Component;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;


@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    /**
     * 登录成功的处理
     */
    @Autowired
    private MyAuthenticationSuccessHandler successHandler;

    /**
     * 登录失败的处理
     */
    @Autowired
    private MyAuthenticationFailureHandler failureHandler;

    /**
     * 数据库验证用户信息
     */
    @Autowired
    private UserService userService;

    @Autowired
    private SmsCodeFilter smsCodeFilter;

    @Autowired
    private SmsCodeAuthenticationSecurityConfig codeAuthenticationSecurityConfig;

    //配置加密的方式
    @Bean
    PasswordEncoder passwordEncoder() {
        return NoOpPasswordEncoder.getInstance();
    }

    //配置认证用户信息和授权
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userService);
    }

    //配置拦截请求资源
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                //开启HttpSecurity配置
                .authorizeRequests()
                //指定路径
                .antMatchers("/login.html").permitAll()
                .antMatchers("/sendCode").permitAll()
                .antMatchers("/mobile").permitAll()
                .antMatchers("/**").fullyAuthenticated()
                //配置认证模式
                .and().formLogin()
                .loginPage("/login.html")
                .loginProcessingUrl("/mobile")
                //登录成功的操作
                .successHandler(successHandler)
                //登录失败的操作
                .failureHandler(failureHandler)
                .and()
                .logout()
                .logoutUrl("/logout")
                //清除身份认证信息
                .clearAuthentication(true)
                //设置session失效
                .invalidateHttpSession(true)
                .addLogoutHandler(new LogoutHandler() {
                    @Override
                    public void logout(HttpServletRequest req, HttpServletResponse resp, Authentication auth) {
                    }
                })
                .logoutSuccessHandler(new LogoutSuccessHandler() {
                    @Override
                    public void onLogoutSuccess(HttpServletRequest req, HttpServletResponse resp, Authentication auth) throws IOException, ServletException {
                        //退出成功后跳转到登录
                        resp.sendRedirect("/login.html");
                    }
                })
                //配置和登录相关的接口不需要认证
                .permitAll()
                .and()
                //关闭cors
                .csrf()
                .disable();
                //加载自己的配置
        http.apply(codeAuthenticationSecurityConfig);
        http.addFilterBefore(smsCodeFilter, UsernamePasswordAuthenticationFilter.class);


    }
}

 8、登录成功、失败异常处理

package com.example.springsecurity.handler;

import com.example.springsecurity.domain.User;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.stereotype.Component;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;


/**
 * @Author: yushizhong
 * @Date: 2020/1/7 10:11
 * @Title: 登录成功处理
 */
@Component
public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
    @Override
    public void onAuthenticationSuccess(HttpServletRequest req, HttpServletResponse resp, Authentication auth) throws IOException, ServletException {
        //这里可以进行页面的跳转或返回json数据给客户端浏览器
        User principal = (User) auth.getPrincipal();//获取登录用户的信息
        principal.setPassword(null);
        resp.setContentType("application/json;charset=utf-8");
        PrintWriter out=resp.getWriter();
        resp.setStatus(200);
        Map<String,Object> map=new HashMap<>();
        map.put("status",200);
        map.put("msg",principal);
        ObjectMapper objectMapper = new ObjectMapper();
        out.write(objectMapper.writeValueAsString(map));
        out.flush();;
        out.close();
//        resp.sendRedirect("/");
    }
}

package com.example.springsecurity.handler;

import com.example.springsecurity.exception.ImgException;
import com.example.springsecurity.exception.SmsCodeException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.security.authentication.*;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.stereotype.Component;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;


/**
 * @Author: yushizhong
 * @Date: 2020/1/7 10:10
 * @Title: 验证失败处理
 */
@Component
public class MyAuthenticationFailureHandler implements AuthenticationFailureHandler {
    @Override
    public void onAuthenticationFailure(HttpServletRequest req, HttpServletResponse resp, AuthenticationException e) throws IOException, ServletException {
        resp.setContentType("application/json;charset=utf-8");
        PrintWriter out=resp.getWriter();
        resp.setStatus(401);
        Map<String,Object> map=new HashMap<>();
        map.put("status",401);
        if(e instanceof LockedException){
            map.put("msg","账户被锁定,无法登录");
        }else if(e instanceof BadCredentialsException){
            map.put("msg","用户名或密码错误");
        }else if(e instanceof DisabledException){
            map.put("msg","账户被禁用,无法登录");
        }else if(e instanceof AccountExpiredException){
            map.put("msg","账户已过期,无法登录");
        }else if(e instanceof CredentialsExpiredException){
            map.put("msg","密码已过期,无法登录");
        }else if(e instanceof SmsCodeException){
            map.put("msg",e.getMessage());
        }else{
            map.put("msg","登录异常,请联系管理员");
        }
        ObjectMapper objectMapper = new ObjectMapper();
        out.write(objectMapper.writeValueAsString(map));
        out.flush();;
        out.close();
//        resp.sendRedirect("/login.html");
    }
}

package com.example.springsecurity.exception;

import org.springframework.security.core.AuthenticationException;
import org.springframework.stereotype.Component;

/**
 * @Author: yushizhong
 * @Date: 2020/1/9 15:03
 * @Title: 短信验证码异常类
 */
public class SmsCodeException  extends AuthenticationException {
    public SmsCodeException(String msg) {
        super(msg);
    }
}

 9、登录页面login.html

10、短信发送的模拟接口

package com.example.springsecurity.controller;

import com.example.springsecurity.util.CodeUtil;
import com.example.springsecurity.util.SendSms;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.servlet.http.HttpServletRequest;

@Controller
public class UserController {
    @RequestMapping("/sendCode")
    public void sendCode(HttpServletRequest request,String mobile){
        String code = CodeUtil.getCode(6);
        System.out.println("验证码:"+code);
//        SendSms.sendMsg(mobile,code);
       request.getSession().setAttribute("code",code);
    }
}

 11、创建UserService类,重写验证的方法loadUserByUsername。

package com.example.springsecurity.service;

import com.example.springsecurity.domain.Auth;
import com.example.springsecurity.domain.User;
import com.example.springsecurity.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;


@Service
public class UserService implements UserDetailsService {
    @Autowired
    private UserMapper mapper;

    @Override
    public UserDetails loadUserByUsername(String mobile) throws UsernameNotFoundException {
        User user=mapper.loadUserByMobile(mobile);
        if(user==null){
            throw  new UsernameNotFoundException("用户不存在");
        }
        return user;
    }

}

标签:spring,boot,springframework,---,import,org,security,com,public
来源: https://blog.csdn.net/liwei10822/article/details/122275938

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有