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应用springboot成神之——Basic Auth应用 - qz奔跑的马 - 博客园

Spring Boot之拦截器与过滤器(完整版) :Spring Boot之拦截器与过滤器(完整版) - 一飞要上天 - 博客园

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

cy谭

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值