Spring Security权限框架理论与简单Case

Spring Security权限管理框架介绍

Spring Security 提供了基于javaEE的企业应用软件全面的安全服务。这里特别强调支持使用Spring框架构件的项目,Spring框架是企业软件开发javaEE方案的领导者。若是你尚未使用Spring来开发企业应用程序,咱们热忱的鼓励你仔细的看一看。熟悉Spring特别是一来注入原理两帮助你更快更方便的使用Spring Security。css

人们使用Spring Secruity的缘由有不少,单大部分都发现了javaEE的Servlet规范或EJB规范中的安全功能缺少典型企业应用场景所需的深度。提到这些规范,重要的是要认识到他们在WAR或EAR级别没法移植。所以若是你更换服务器环境,这里有典型的大量工做去从新配置你的应用程序员安全到新的目标环境。使用Spring Security 解决了这些问题,也为你提供许多其余有用的,可定制的安全功能。html

正如你可能知道的两个应用程序的两个主要区域是“认证”和“受权”(或者访问控制)。这两个主要区域是Spring Security 的两个目标。“认证”,是创建一个他声明的主题的过程(一个“主体”通常是指用户,设备或一些能够在你的应用程序中执行动做的其余系统)。“受权”指肯定一个主体是否容许在你的应用程序执行一个动做的过程。为了抵达须要受权的目的,主体的身份已经有认证过程创建。这个概念是通用的而不仅在Spring Security中。java

Spring Security主要的组件图:
Spring Security权限框架理论与简单Case程序员

在身份验证层,Spring Security 的支持多种认证模式。这些验证绝大多数都是要么由第三方提供,或由相关的标准组织,如互联网工程任务组开发。另外Spring Security 提供本身的一组认证功能。具体而言,Spring Security 目前支持全部这些技术集成的身份验证:web

  • HTTP BASIC 认证头 (基于 IETF RFC-based 标准)
  • HTTP Digest 认证头 ( IETF RFC-based 标准)
  • HTTP X.509 客户端证书交换 ( IETF RFC-based 标准)
  • LDAP (一个很是常见的方法来跨平台认证须要, 尤为是在大型环境)
  • Form-based authentication (用于简单的用户界面)
  • OpenID 认证
  • Authentication based on pre-established request headers (such as Computer Associates Siteminder) 根据预先创建的请求有进行验证
  • JA-SIG Central Authentication Service (CAS,一个开源的SSO系统 )
  • Transparent authentication context propagation for Remote Method Invocation (RMI) and HttpInvoker (Spring远程协议)
  • Automatic "remember-me" authentication (你能够勾选一个框以免预约的时间段再认证)
  • Anonymous authentication (让每个未经验证的访问自动假设为一个特定的安全标识)
  • Run-as authentication (在一个访问应该使用不一样的安全标识时很是有用)
  • Java Authentication and Authorization Service (JAAS)
  • JEE container autentication (因此若是愿你以能够任然使用容器管理的认证)
  • Kerberos
  • Java Open Source Single Sign On (JOSSO) *
  • OpenNMS Network Management Platform *
  • AppFuse *
  • AndroMDA *
  • Mule ESB *
  • Direct Web Request (DWR) *
  • Grails *
  • Tapestry *
  • JTrac *
  • Jasypt *
  • Roller *
  • Elastic Path *
  • Atlassian Crowd *
  • Your own authentication systems (see below)

Spring Security5.x官方文档地址以下:spring

https://docs.spring.io/spring-security/site/docs/5.0.7.RELEASE/reference/htmlsingle/
https://docs.spring.io/spring-security/site/docs/5.0.7.RELEASE/api/数据库


Spring Security经常使用的11个权限拦截器

Spring Security权限框架理论与简单Case

SecurityContextPersistenceFilter:api

  • 这个过滤器位于顶端,是第一个起做用的过滤器
  • 验证用户session是否存在,存在则放到SecurityContextHolder中,不存在则建立后到SecurityContextHolder中
  • 另外一个做用是在过滤器执行完毕后清空SecurityContextHolder中的内容

LogoutFilter:安全

  • 在用户发出注销请求时,清除用户的session以及SecurityContextHolder中的内容

AbstractAuthenticationProcessingFilter:bash

  • 处理from表单登陆的过滤器

DefaultLoginPageGeneratingFilter:

  • 用来生成一个默认的登陆页面

BasicAuthenticationFilter:

  • 用于Basic验证

SecurityContextHolderAwareRequestFilter:

  • 用于包装用户的请求
  • 目的是为后续的程序提供一些额外的数据

RememberMeAuthenticationFilter:

  • 当用户cookie中存在RememberMe标记时,会根据标记自动实现用户登陆,并建立SecurityContext,授予相应的权限

AnonymousAuthenticationFilter:

  • 保证操做统一性,当用户没有登陆时,默认为用户分配匿名用户的权限,能够选择关闭匿名用户

ExceptionTranslationFilter:

  • 处理FilterSecurityInterceptor中所抛出的异常,而后将请求重定向到相应的页面,或响应错误信息。也就是做为一个处理全局异常的Filter

SessionManagementFilter:

  • 用于防护会话伪造×××,会销毁用户的session,并从新生成一个session

FilterSecurityInterceptor:

  • 用户的权限控制都包含在这里
  • 若是用户未登录就会抛出用户未登录的异常
  • 若是用户已登陆可是没有访问当前资源的权限,就会抛出拒绝访问异常
  • 若是用户已登陆并具备访问当前资源的权限,则放行

以上就是Spring Security经常使用的11个权限拦截器,那么这些拦截器是按什么样的顺序执行的呢?这就须要先了解一下FilterChainProxy这个过滤器链代理类了:

  • FilterChainProxy能够按照指定的顺序调用一组Filter,使这组Filter既能完成验证受权的本职工做,又能享用Spring IOC的功能,来方便的获得其余依赖的资源

基于SpringBoot的SpringSecurity环境快速搭建

打开IDEA,建立一个SpringBoot项目:
Spring Security权限框架理论与简单Case
Spring Security权限框架理论与简单Case

勾选相应的模块:
Spring Security权限框架理论与简单Case

在项目中新建一个config包,在该包下建立 SpringSecurityConfig 配置类,用于配置Spring Security的拦截规则。代码以下:

package org.zero.security.securitydemo.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

/**
 * @program: security-demo
 * @description: Spring Security 配置类
 * @author: 01
 * @create: 2018-08-29 23:20
 **/
@Configuration
@EnableWebSecurity
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // 定义一个简单的访问规则
        http.authorizeRequests()
                .antMatchers("/").permitAll()  // 容许任意访问根路径
                .anyRequest().authenticated()  // 除根路径之外的请求都须要验证
                .and().logout().permitAll()  // 容许任意访问注销路径
                .and().formLogin();  // 容许表单登陆

        // 禁用默认的csrf验证
        http.csrf().disable();
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        // 不拦截静态资源的访问
        web.ignoring().antMatchers("/js/**", "/css/**", "/images/**");
    }
}

而后新建一个controller包,在该包中建立 DemoController 控制器类,用于开启一些接口进行测试。代码以下:

package org.zero.security.securitydemo.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @program: security-demo
 * @description: spring security demo
 * @author: 01
 * @create: 2018-08-29 23:14
 **/
@RestController
public class DemoController {

    @GetMapping("/")
    public String demo(){
        return "Hello Spring Security";
    }

    @GetMapping("/hello")
    public String hello(){
        return "Hello World";
    }
}

启动项目,访问根目录,正常输出了相应的字符串:
Spring Security权限框架理论与简单Case

而访问/hello,就会跳转到登陆页面,须要进行验证,这就表明SpringSecurity的配置:
Spring Security权限框架理论与简单Case


基于SpringSecurity权限管理Case实操

Case一、简单的登陆:

SpringSecurity自带有一套基于内存的验证,这样咱们仅须要实现简单的登陆功能的时候,就不须要额外去建立数据库了。在 SpringSecurityConfig 类中,加入以下方法:

@Configuration
@EnableWebSecurity
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication().  // 使用基于内存的认证方式
                passwordEncoder(new BCryptPasswordEncoder()).  // 设置密码的加密方式
                withUser("admin").  // 设置用户名称
                password(new BCryptPasswordEncoder().encode("123456")).  // 设置密码
                roles("ADMIN");  // 自定义该用户的角色
    }
    ...
}

重启项目,当访问受控制的资源时,就会跳转到以下登陆页面,输入设定好的用户名和密码:
Spring Security权限框架理论与简单Case

访问成功:
Spring Security权限框架理论与简单Case

访问logout接口能够退出登陆:
Spring Security权限框架理论与简单Case


Case二、有指定的角色,每一个角色有指定的权限:

即使是简单的登陆,也可能会遇到有一些资源须要管理员角色才能访问。因此咱们来看看如何限定一个资源只能被管理员用户访问。在configure方法中,增长一个普通用户,代码以下:

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.inMemoryAuthentication().
            passwordEncoder(new BCryptPasswordEncoder()).
            withUser("admin").
            password(new BCryptPasswordEncoder().encode("123456")).
            roles("ADMIN");

    auth.inMemoryAuthentication().
            passwordEncoder(new BCryptPasswordEncoder()).
            withUser("user").
            password(new BCryptPasswordEncoder().encode("user")).
            roles("USER");
}

在DemoController类中增长一个接口,并指定这个接口只能被admin角色的用户访问。代码以下:

@RestController
// 开启验证
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class DemoController {

    // 指定该接口只能被ADMIN角色的用户访问,ROLE_这个前缀是固定要写的
    @PreAuthorize("hasRole('ROLE_ADMIN')")  
    @GetMapping("/roleAuth")
    public String role(){
        return "admin auth";
    }
    ...
}

重启项目,登陆非admin角色的用户:
Spring Security权限框架理论与简单Case

访问roleAuth接口,会返回403错误:
Spring Security权限框架理论与简单Case

登陆admin用户,访问roleAuth接口成功:
Spring Security权限框架理论与简单Case

@PreAuthorize里的表达式可使用 and 、or这种运算符,例如:

@PreAuthorize("hasRole('ROLE_ADMIN') or hasRole('ROLE_ROOT')")

除了@PreAuthorize注解外,还有:

  • @PostAuthorize:方法执行完后再进行角色验证
  • @PreFilter:方法执行前进行验证,用于过滤集合类型的参数或返回值
  • @PostFilter:方法执行后进行验证,用于过滤集合类型的参数或返回值

Case三、自定义密码加密:

咱们能够自定义本身的加密方式去作密码的加密及匹配,我这里使用MD5做为演示。首先建立一个 MyPasswordEncoder 类并实现 PasswordEncoder 接口。具体代码以下:

package org.zero.security.securitydemo.encoder;

import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.util.DigestUtils;

/**
 * @program: security-demo
 * @description: 自定义密码加密器
 * @author: 01
 * @create: 2018-09-07 21:43
 **/
public class MyPasswordEncoder implements PasswordEncoder {

    @Override
    public String encode(CharSequence charSequence) {
        // 使用Spring自带的工具进行MD5加密
        return DigestUtils.md5DigestAsHex(charSequence.toString().getBytes());
    }

    @Override
    public boolean matches(CharSequence rawPassword, String encodedPassword) {        
        // 验证密码加密后是否一致
        return encode(rawPassword).equals(encodedPassword);
    }
}

使用咱们本身自定义的密码加密器,修改configure方法的代码以下:

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.inMemoryAuthentication().
            passwordEncoder(new MyPasswordEncoder()).
            withUser("admin").
            password(new MyPasswordEncoder().encode("123456")).
            roles("ADMIN");

    auth.inMemoryAuthentication().
            passwordEncoder(new MyPasswordEncoder()).
            withUser("user").
            password(new MyPasswordEncoder().encode("user")).
            roles("USER");
}

Case四、参数验证:

经过@PreAuthorize注解,咱们能够在方法执行前,进行权限参数的验证。例如我要验证id小于时,且username参数的值和当前登陆的用户名一致。代码以下:

@PreAuthorize("#id<10 and principal.username.equals(#username)")
@GetMapping("/check_info")
public String checkInfo(Integer id, String username) {
    return "success";
}

若是参数是一个对象也能够进行校验,代码以下:

@PreAuthorize("#user.username.equals('admin')")
@GetMapping("/check_user")
public String checkUser(User user) {
    return "success";
}

总结

优势:

  • 提供了一套安全框架,并且这个框架是能够用的
  • 提供了不少用户认证功能,实现相关接口便可,节约大量开发工做
  • 基于Spring,使得它易于集成到Spring项目中,且封装了许多方法

缺点:

  • 配置文件多,角色被 “编码” 到配置文件或源文件中,RBAC不明显
  • 对于系统中用户、角色、权限之间的关系,没有可操做的界面
  • 大数据量的状况下,几乎不可用
相关文章
相关标签/搜索