美文网首页
简述 Spring Security 框架及其不足

简述 Spring Security 框架及其不足

作者: 春秋杰瑞 | 来源:发表于2019-09-29 09:14 被阅读0次

    简介

    Spring Security 是一个拥有高度可定制或的认证和授权机制的安全框架,同时更是守护基于 Spring 应用「事实上的」安全标准。

    它支持开箱即用,只需简单配置即可保护应用远离黑客攻击,支持以下功能:

    • 防护会话固定攻击(Session Fixation)、点击劫持(Clickjacking)以及跨站请求伪造(CSRF)等常见攻击
    • 支持表单登录、OAuth 2.0 登录
    • 认证方式多种多样,支持基于内存(In-Memory)认证、基于JDBC 数据库认证、基于 LDAP 认证或者自定义的认证方式
    • 集成 Servlet API,支持 HttpServletRequest.logout() login() 等方法
    • 记住我认证

    完整介绍可以参考Spring Security 白皮书

    认证与授权

    上述功能可能仍然不满足实际需要,因此有必要了解 Spring Security 认证和授权大体流程以便实现定制化。

    有的读者可能认为认证与授权是一回事,实际上两者的界限非常清晰。认证是指验证登录用户身份是否合法,而授权则是验证用户能否访问的指定资源。

    Spring Security 认证流程如下:

    1. 用户输入用户名和密码,形成用户名和密码令牌 UsernamePasswordAuthenticationToken
    2. 将令牌传递给认证管理器(AuthenticationManager)验证是否有效;
    3. 认证成功后,认证管理器返回一个包含必需信息的认证令牌;
    4. 此时调用 SecurityContextHolder.getContext().setAuthentication(…​) 方法为用户建立安全上下文,放入刚刚得到的认证令牌。

    Spring Security 授权流程如下:

    1. 从安全上下文容器(SecurityContextHolder)获取认证令牌 (Authentication );
    2. 通过在安全元信息源(SecurityMetadataSource)中查找对比用户的认证令牌 (Authentication )来判断当前请求申请公开资源还是安全资源;
    3. 如果是安全资源,则询问访问控制器(AccessDecisionManager)是否允许访问;
    4. 如果是开放资源,则直接允许访问。

    Spring Security 通过 Web 安全配置适配器 WebSecurityConfigurerAdapter 来暴露配置接口,可配置项极其丰富。具体如何配置以及每项配置用途可参看刚刚提到的白皮书或 Javadoc。

    Ajax 登录

    Spring Security 默认支持表单登录,如果你的项目使用 Ajax 来实现用户登录则需要按以下步骤进行配置。

    1. 首先,继承 Web 安全配置适配器 WebSecurityConfigurerAdapter 来配置 HttpSecurity 对象,这个对象主要用来配置 HTTP 认证和授权相关选项。

    我们要配置的地方比较多比较杂,因此提供如下参考代码:

    package me.jerrychin.spring.security.demo;
    
    import org.apache.logging.log4j.LogManager;
    import org.apache.logging.log4j.Logger;
    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;
    
    /**
     * 配置应用安全相关功能
     */
    @EnableWebSecurity
    public class SecurityConfig extends WebSecurityConfigurerAdapter {
        private static final Logger logger = LogManager.getLogger(SecurityConfig.class);
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            // 暂时禁用 csrf
            http.csrf().disable().authorizeRequests()
                    // 允许访问 /public 下方所有路径
                    .antMatchers("/public/**").permitAll()
                    .and()
                    .formLogin() // 开启表单登录(我们实际上是通过 Ajax 来登录)
                    .loginPage("/login/page") // 登录主页,由于我们是 Ajax 登录,这里的主页实际上一串 JSON
                    .failureUrl("/login/fail") // 失败时,重定向客户端用户到此地址。failureForwardUrl 和 failureUrl 只能设置一个
                    .successForwardUrl("/login/success") // 认证成功对于的处理页面(注意这里的 FORWARD 时内部转跳)
                    .failureForwardUrl("/login/fail") // 认证失败对于的处理页面(注意这里的 FORWARD 时内部转跳)
                    .loginProcessingUrl("/login") // 处理登录 POST 请求的接口名,Spring Security 已经帮我们实现
                    .passwordParameter("password") // 密码对应的参数名
                    .usernameParameter("username") // 用户名对应的参数名
                    
                    // 允许访问所有登录相关的URL
                    .permitAll();
        }
        
            @Override
        protected void configure(AuthenticationManagerBuilder auth) throws Exception {
            
            // 你可以使用这里配置的用户凭据登录
            auth.inMemoryAuthentication().withUser("user").password("password").roles("USER")
                   .and().withUser("admin").password("password").roles("USER", "ADMIN");
        }
        
    }
    
    

    上述配置出现了多个 URL,这些 URL对应的接口都是需要我们自己来实现。比如认证失败,我们肯定想向客户端返回相应的错误代码和原因。

    下面的代码实现了上述 URL以便 Spring Security 适时调用,主要目的是向客户端响应合适的 JSON 认证结果。

    package me.jerrychin.spring.security.demo;
    
    import com.alibaba.fastjson.JSONObject;
    import com.eques.eqhome.commons.ErrorCode;
    import org.apache.logging.log4j.LogManager;
    import org.apache.logging.log4j.Logger;
    import org.springframework.security.authentication.BadCredentialsException;
    import org.springframework.security.core.AuthenticationException;
    import org.springframework.security.web.WebAttributes;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.ResponseBody;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    /**
     * Ajax 形式的登录控制器,主要用于响应认证成功或失败
     *
     *
     * @author Jerry Chin
     */
    @Controller
    @ResponseBody
    @RequestMapping(path = "/login")
    public class LoginController {
        private static final Logger logger = LogManager.getLogger(LoginController.class);
    
        // 登录主页
        @RequestMapping("/page")
        public JSONObject handleLoginPage() {
    
            JSONObject object = new JSONObject();
            object.put("reason", "未登录");
            object.put("code", 100);
            return object;
        }
    
        // 登录成功
        @RequestMapping("/success")
        public JSONObject handleSuccessLogin(HttpServletRequest request, HttpServletResponse response) {
    
            JSONObject object = new JSONObject();
            object.put("reason", "登录成功");
            object.put("code", 0);
    
            return object;
        }
    
        // 登录失败
        @RequestMapping("/fail")
        public JSONObject handleFailLogin(HttpServletRequest request) {
            
            AuthenticationException ex = (AuthenticationException) request.getAttribute(WebAttributes.AUTHENTICATION_EXCEPTION);
            
            JSONObject object = new JSONObject();
            if (ex instanceof BadCredentialsException) {
                object.put("reason", "账号或密码错误");
                object.put("code", ErrorCode.UNAUTHORIZED.code);
            } else {
                object.put("reason", "未知错误");
                object.put("code", ErrorCode.UNKNOWN.code);
            }
    
            return object;
        }
    }
    

    这里其实省略了很多步骤,比如如何建立 Spring Security 项目环境,由于这些事情跟本文关系不大,所以你必须自己搞定。

    动态授权

    Spring Security 的授权配置十分灵活,但存在一个致命缺陷————无法动态更新授权配置,这代表什么意思呢?
    很简单,一旦应用启动则无法修改或重新加载新的授权配置,除非....重启应用!这显然太沙雕了。

    不过,Spring Security 提供了强大的定制化接口,就看你动手能力强不强了。

    ...待续未完

    相关文章

      网友评论

          本文标题:简述 Spring Security 框架及其不足

          本文链接:https://www.haomeiwen.com/subject/ixewuctx.html