• springboot Basic Auth 暴露API 访问认证


    因为 Basic Auth 的身份信息是写在请求中,被截获账号密码可能会泄露,为此增加一重ip认证

    在实际应用中,可能会用spring boot  写一些微服务去做底层的一些预处理,然后再开放一些接口传输数据。为了安全,同城要做一些访问的认证,也不用选太复杂的认证方式,就用 Basic Auth就可以,再在此基础上再做一些认证,比如这里的ip。

    为此,需要两个方面的思考

    1、如何做 Basic Auth 的认证
    2、如何检验访问者的ip并授权

    下面通过代码说明

    一、依赖

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>

    二、控制器Controller

    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.security.core.Authentication;
    import org.springframework.security.core.context.SecurityContextHolder;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    import org.springframework.web.bind.annotation.ResponseBody;
    import org.springframework.web.bind.annotation.RestController;
     
    import javax.servlet.http.Cookie;
    import javax.servlet.http.HttpServletRequest;
     
    /**
     * api
     */
    @RestController
    @RequestMapping("/translate")
    public class TranslateController {
        @ResponseBody
        @RequestMapping(value = "/AuthTest", method = RequestMethod.GET)
        public String AuthTest() {
            Authentication auth = SecurityContextHolder.getContext().getAuthentication();
            System.out.println(auth.getName());
            return "OK";
        }
     
    }

    三、匿名用户访问无权限资源时的异常处理 类

    import org.springframework.security.core.AuthenticationException;
    import org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint;
    import org.springframework.stereotype.Component;
     
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.IOException;
    import java.io.PrintWriter;
     
    /**
     * 匿名用户访问无权限资源时的异常处理
     * 重写commence,处理异常
     * 当 认证失败时 会跳转到  commence 方法,所以这里可以做一些定制化
     */
    @Component
    public class Authenication extends BasicAuthenticationEntryPoint {
        @Override
        public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authEx) throws IOException {
            response.addHeader("WWW-Authenticate", "Basic realm=" + getRealmName());
            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            PrintWriter writer = response.getWriter();
            writer.println("账号密码不正确 HTTP Status 401 - " + authEx.getMessage());
        }
     
        @Override
        public void afterPropertiesSet() {
            setRealmName("translate");
            super.afterPropertiesSet();
        }
    }

    四、web 安全认证配置 类

    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Value;
    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.authentication.configurers.provisioning.InMemoryUserDetailsManagerConfigurer;
    import org.springframework.security.config.annotation.web.builders.HttpSecurity;
    import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
    import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
    import org.springframework.security.core.userdetails.User;
    import org.springframework.security.core.userdetails.UserDetails;
    import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
    import org.springframework.security.web.AuthenticationEntryPoint;
    import org.springframework.web.servlet.config.annotation.CorsRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
     
    @Configuration
    @EnableWebSecurity
    public class WebSecurityConfig extends WebSecurityConfigurerAdapter implements WebMvcConfigurer {
        @Value("${myname}")
        private String myname;
        @Value("${mypassword}")
        private String mypassword;
        private final static Logger log = LoggerFactory.getLogger(WebSecurityConfig.class);
        @Autowired
        private AuthenticationEntryPoint authEntryPoint;
     
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            // 关闭跨域保护
            http.cors().and().csrf().disable();
            // 所有的请求都要验证
            http.authorizeRequests().anyRequest().authenticated();
            // 使用authenticationEntryPoint验证 user/password
            http.httpBasic().authenticationEntryPoint(authEntryPoint);
        }
     
        @Bean
        public BCryptPasswordEncoder passwordEncoder() {
            BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
            return bCryptPasswordEncoder;
        }
     
        /**
         * 配置授权的 账号密码
         * 这里是在配置文件配置好
         *
         * @param auth
         * @throws Exception
         */
        @Autowired
        public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
     
            log.info("user: " + myname);
            log.info("password: " + mypassword);
            String encrytedPassword = this.passwordEncoder().encode(mypassword);
            System.out.println("Encoded password = " + encrytedPassword);
     
            // 这里使用写死的验证
            InMemoryUserDetailsManagerConfigurer<AuthenticationManagerBuilder> mngConfig = auth.inMemoryAuthentication();
            UserDetails u1 = User.withUsername(myname).password(encrytedPassword).roles("ADMIN").build();
     
            mngConfig.withUser(u1);
        }
     
        @Override
        public void addCorsMappings(CorsRegistry registry) {
            registry.addMapping("/**").allowedOrigins("*").allowedMethods("GET", "POST", "PUT", "DELETE").allowedOrigins("*")
                    .allowedHeaders("*");
        }
    }

    五、配置文件  application.yml

    server:
      port: 9999
      servlet:
        context-path: /translate-web/
     
    #请求账号密码
    myname: test
    mypassword: 123456
     
    #授权ips,逗号隔开
    ipAuthSwitch: true
    ips: 192.168.1.2,0:0:0:0:0:0:0:1

    六、postman 访问(带上认证信息)

    至此,整个Basic Auth认证就完成了 

    下面我们在上面的基础上补充ip认证 

    原理就是用拦截器拦截请求,然后在请求中获取ip,将这个ip和配置授权的ip做对比,符合就通过,否则不允许请求

    七、自定义拦截器

    import org.apache.commons.lang3.StringUtils;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.lang.Nullable;
    import org.springframework.web.servlet.HandlerInterceptor;
    import org.springframework.web.servlet.ModelAndView;
     
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.util.Set;
     
    /**
     * 拦截器
     */
    public class TranslateInterceptor implements HandlerInterceptor {
     
        private final static Logger log = LoggerFactory.getLogger(TranslateInterceptor.class);
     
        long start = System.currentTimeMillis();
     
        private Set<String> ips;
     
        private Boolean ipAuthSwitch;
     
        public TranslateInterceptor( Set<String> ips, Boolean ipAuthSwitch) {
            this.ips = ips;
            this.ipAuthSwitch = ipAuthSwitch;
        }
     
        /**
         * preHandle是在请求执行前执行的
         *
         * @param request
         * @param response
         * @param handler
         * @return
         * @throws Exception
         */
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            start = System.currentTimeMillis();
     
            String ip = request.getRemoteAddr();
            log.info("request ip: " + ip);
     
            /**
             * 返回true,postHandler和afterCompletion方法才能执行
             * 否则false为拒绝执行,起到拦截器控制作用
             */
            if (ipAuthSwitch) {
                if(StringUtils.isNotEmpty(ip) && ips.contains(ip)){
                    return true;
                }else{
                    log.info("ip:{} No authority", ip);
                    return false;
                }
            }else{
                return true;
            }
        }
     
        /**
         * postHandler是在请求结束之后,视图渲染之前执行的,但只有preHandle方法返回true的时候才会执行
         *
         * @param request
         * @param response
         * @param handler
         * @param modelAndView
         * @throws Exception
         */
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
            System.out.println("Interception cost=" + (System.currentTimeMillis() - start));
        }
     
        /**
         * afterCompletion是视图渲染完成之后才执行,同样需要preHandle返回true
         *
         * @param request
         * @param response
         * @param handler
         * @param ex
         * @throws Exception
         */
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
            //该方法通常用于清理资源等工作
        }
     
    }

    八、拦截器配置

    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
     
    import java.util.Arrays;
    import java.util.HashSet;
    import java.util.Set;
     
    /**
     * 拦截器配置
     */
    @Configuration
    public class InterceptorConfig extends WebMvcConfigurationSupport {
     
        @Value("${ips}")
        private String ips;
        @Value("${ipAuthSwitch}")
        private Boolean ipAuthSwitch;
     
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
     
            String[] split = ips.split(",");
            Set<String> ipSet = new HashSet<>(Arrays.asList(split));
     
            registry.addInterceptor(new TranslateInterceptor(ipSet, ipAuthSwitch))
                    //添加需要验证登录用户操作权限的请求
                    .addPathPatterns("/**")
                    //这里add为“/**”,下面的exclude才起作用,且不管controller层是否有匹配客户端请求,拦截器都起作用拦截
                    //排除不需要验证登录用户操作权限的请求
                    .excludePathPatterns("/wang")
                    .excludePathPatterns("/css/**")
                    .excludePathPatterns("/js/**")
                    .excludePathPatterns("/images/**");
            //这里可以用registry.addInterceptor添加多个拦截器实例,后面加上匹配模式
            super.addInterceptors(registry);//最后将register往这里塞进去就可以了
        }
    }

    最后感谢两位博主的资料

    springboot成神之——Basic Auth应用:https://www.cnblogs.com/ye-hcj/p/9632694.html

    Spring Boot之拦截器与过滤器(完整版) :https://www.cnblogs.com/yifeiyaoshangtian/p/10280808.html

  • 相关阅读:
    elk 介绍与logstash插件
    es 中文分词器IK
    Swift编译慢?请看这里,全套开源
    你与 “顶级iOS工程师” 距离有多远?
    iOS工程师如何在工作中提升自己?
    iOS技术人的出路在哪里,35岁被优化吗?
    菜鸟摇身变大牛
    iOS-MobLink集成流程
    iOS进阶之页面性能优化
    从零讲解 iOS OpenGL ES 的纹理渲染 原来是泽镜啊
  • 原文地址:https://www.cnblogs.com/wpcnblog/p/15165800.html
Copyright © 2020-2023  润新知