zoukankan      html  css  js  c++  java
  • Spring Security 之API 项目安全验证(基于basic-authentication)

    ===================================
    Basic Authorization 规范
    ===================================
    Request 头部:
    Authorization: Basic QWxpY2U6MTIzNDU2
    其中 QWxpY2U6MTIzNDU2 是user:pwd做 base64 编码, 格式是 user:pwd

    response 头部:
    WWW-Authenticate: Basic realm="My Realm"

    按照 RFC 规范, 相同的 realm(域) 下的web page 将共享同样的 credentials, 所以推荐 realm 取值为 application name. realm 大小写敏感, 可以包含空格.


    ===================================
    Rest API 示例
    ===================================
    功能:
    1. 演示如何启用 Basic Authorization
    2. 如何使用 RestTemplate 访问受保护的 API接口

    -----------------------------------
    SecurityConfig 代码
    -----------------------------------
    关键点有:
    0. 对于 /api/** 需要 ROLE_ADMIN 角色的账号访问, 对于 /guest/** 路径允许匿名访问.
    1. 使用 HttpSecurity.httpBasic() 启用 Basic Authorization.
    2. 使用 HttpSecurity.httpBasic().realmName() 设置 realm.
    3. 使用 HttpSecurity.httpBasic().authenticationEntryPoint() 设置 BasicAuthenticationEntryPoint 对象, 如果一个请求通过验证, 该对象会自动为web response设定 WWW-Authenticate header, 如果未通过, 该对象会自动将HttpStatus设置为UNAUTHORIZED.
    4. 显式启用了 STATELESS session 管理机制, 经测试,Spring Security 在Basic Authorization模式下, session自动就处于了 STATELESS 状态.
    5. 对于 HttpMethod.OPTIONS 请求, 允许匿名访问. API 项目应该开放 OPTIONS 查询权限.

    @EnableWebSecurity
    @EnableGlobalMethodSecurity(prePostEnabled = true)
    public class SecurityConfig extends WebSecurityConfigurerAdapter {
    
        //@formatter:off
        @Override
        public void configure(AuthenticationManagerBuilder builder) throws Exception {
            builder.inMemoryAuthentication()
                    .withUser("123").password("123").roles("USER")
                    .and()
                    .withUser("ADMIN").password("ADMIN").roles("ADMIN");
        }
        //@formatter:on
    
        private static String REALM = "MY SPRING SECURITY DEMO";
    
        // @formatter:off
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http
               .authorizeRequests()
                   // 对于/api 路径下的访问需要有 ROLE_ADMIN 的权限
                  .antMatchers("/api/**").hasRole("ADMIN")
                   // 对于/guest 路径开放访问
                  .antMatchers("/guest/**").permitAll()
                   // 其他url路径之需要登陆即可.
                  .anyRequest().authenticated()
                  .and()
               //启用 basic authentication
              .httpBasic().realmName(REALM).authenticationEntryPoint(getBasicAuthenticationEntryPoint())
                  .and()
               //不创建 session
              .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
        }
        // @formatter:on
    
        /**
         * 生成 BasicAuthenticationEntryPoint 对象, 在该对象的支持下, 通过验证的请求, 返回的response 将会自动加上
         * WWW-Authenticate Header.  在该对象的支持下, 未通过验证的请求, 返回的 response 为 UNAUTHORIZED 错误.
         *
         * @return
         */
        @Bean
        public BasicAuthenticationEntryPoint getBasicAuthenticationEntryPoint() {
            BasicAuthenticationEntryPoint entryPoint = new BasicAuthenticationEntryPoint();
            entryPoint.setRealmName(REALM);
            return entryPoint;
        }
    
        /*
         * 开放 Options 请求
         */
        @Override
        public void configure(WebSecurity web) throws Exception {
            // TODO Auto-generated method stub
            web.ignoring()
                    .antMatchers(HttpMethod.OPTIONS, "/**");
        }
    
        @SuppressWarnings("deprecation")
        @Bean
        public NoOpPasswordEncoder passwordEncoder() {
            BasicAuthenticationEntryPoint a;
            return (NoOpPasswordEncoder) NoOpPasswordEncoder.getInstance();
        }
    }


    -----------------------------------
    受控 API Rest 类
    -----------------------------------
    对于 /api/** url需要 ROLE_ADMIN 角色的账号访问, 这里的代码很简单.

    /*
     * 需要进行 Basic Auth 的API
     */
    @RestController
    @RequestMapping("/api")
    class ApiController {
    @GetMapping(
    "/books") public String getBooks() { return "API book"; } }

    直接访问受控url, 弹出浏览器内置的登陆框, 见下图, 符合预期. 


    -----------------------------------
    RestTemplate 访问 Basic Authorization的API
    -----------------------------------
    关键点:
    使用了 restTemplateBuilder.basicAuthorization(user,pwd).build() 来构建 RestTemplate, 这样的 RestTemplate 会自动在Request上加 Authorization Header.

    /*
     * RestTemplate 访问 Basic Authorization的API的示例
     */
    @RestController
    @RequestMapping("/guest")
    class DefaultController {
    
        @GetMapping("/mybooks")
        @ResponseBody
        public String getMyBook() {
            return "My Book";
        }
    
        private RestTemplate restTemplate;
    
        @Autowired
        private RestTemplateBuilder restTemplateBuilder;
    
        @Autowired
        void setRestTemplate(RestTemplateBuilder restTemplateBuilder) {
             restTemplate = restTemplateBuilder.basicAuthorization("ADMIN", "ADMIN")
             .build();
        }
    
        @GetMapping("/apibooks")
        @ResponseBody
        public String getApiBooks() {
            return restTemplate.getForObject("http://localhost:8080/api/books", String.class);
    
        }
    }

    访问 http://localhost:8080/guest/apibooks 地址,  无需登陆即可得到 api 结果, 见下图, 符合预期. 

    ===================================
    参考
    ===================================
    http://websystique.com/spring-security/secure-spring-rest-api-using-basic-authentication/
    http://www.bytestree.com/spring/restful-web-services-authentication-authorization/
    https://www.baeldung.com/spring-security-basic-authentication

  • 相关阅读:
    POJ 1015 Jury Compromise【DP】
    POJ 1661 Help Jimmy【DP】
    HDU 1074 Doing Homework【状态压缩DP】
    HDU 1024 Max Sum Plus Plus【DP,最大m子段和】
    占坑补题。。最近占的坑有点多。。。
    Codeforces 659F Polycarp and Hay【BFS】
    Codeforces 659E New Reform【DFS】
    Codeforces 659D Bicycle Race【计算几何】
    廖大python实战项目第四天
    廖大python实战项目第三天
  • 原文地址:https://www.cnblogs.com/harrychinese/p/SpringBoot_security_api_basic_auth.html
Copyright © 2011-2022 走看看