SpringSecurity(一)总体结构及使用

一、基本概念

认证authentication

用户认证就是判断一个用户的身份是否合法的过程,用户去访问系统资源时系统要求验证用户的身份信息,身份合法方可继续访问,不合法则拒绝访问。常见的用户身份认证方式有:用户名密码登录,二维码登录,手机短信登录,指纹认证等方式。认证是为了保护系统的隐私数据与资源,用户的身份合法方可访问该系统的资源。

授权authorization

授权是用户认证通过后,根据用户的权限来控制用户访问资源的过程,拥有资源的访问权限则正常访问,没有权限则拒绝访问。认证是为了保证用户身份的合法性,授权则是为了更细粒度的对隐私数据进行划分,授权是在认证通过后发生的,控制不同的用户能够访问不同的资源。

会话session

用户认证通过后,为了避免用户的每次操作都进行认证可将用户的信息保证在会话中。会话就是系统为了保持当前用户的登录状态所提供的机制,常见的有基于session方式、基于token方式等。

二、总体结构

Spring Security是解决安全访问控制的问题,就是认证和授权两个问题。重点是对所有进入系统的请求进行拦截,校验每个请求是否能够访问它所期望的资源,而Spring Security对Web资源的保护是通过Filter来实现的。

当初始化Spring Security时,在WebSecurityConfiguration中会往Spring容器中注入一个名为SpringSecurityFilterChain的Servlet过滤器,类型为org.springframework.security.web.FilterChainProxy,实现了javax.servlet.Filter,外部的请求都会经过这个类。

而FilterChainProxy是一个代理,真正起作用的是FilterChainProxy中SecurityFilterChain所包含的各个Filter,这些Filter都已经注入到Spring容器中。但是他们并不直接处理用户的认证和授权,而是把他们交给了认证管理器(AuthenticationManager)和决策管理器(AccessDecisionManager)进行处理。

public class FilterChainProxy extends GenericFilterBean {
  
  private static final Log logger = LogFactory.getLog(FilterChainProxy.class);
​
  private final static String FILTER_APPLIED = FilterChainProxy.class.getName().concat(
      ".APPLIED");
​
  private List<SecurityFilterChain> filterChains;
  
  ......
public interface SecurityFilterChain {
​
  boolean matches(HttpServletRequest request);
​
  List<Filter> getFilters();
}

Spring Security的功能实现主要就是由一系列过滤器链相互配合完成的。在启动过程中可以看到有info日志:

 Creating filter chain: any request, [org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@16a81431, 
 org.springframework.security.web.context.SecurityContextPersistenceFilter@26d76e45, 
 org.springframework.security.web.header.HeaderWriterFilter@18f38d7a, 
 org.springframework.security.web.authentication.logout.LogoutFilter@e85a71b, 
 org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter@4d0bbae5, 
 cn.exrick.xboot.config.security.jwt.JWTAuthenticationFilter@2e1c5321, 
 org.springframework.security.web.savedrequest.RequestCacheAwareFilter@2f6a0f9c, 
 org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@5437eaca, 
 org.springframework.security.web.authentication.AnonymousAuthenticationFilter@5162cfa4, 
 org.springframework.security.web.session.SessionManagementFilter@6caca8ed, 
 org.springframework.security.web.access.ExceptionTranslationFilter@638f22cc, 
 cn.exrick.xboot.config.security.permission.MyFilterSecurityInterceptor@51e55964, 
 org.springframework.security.web.access.intercept.FilterSecurityInterceptor@116c6746]
​

过滤器链中主要的几个过滤器及其作用:

SecurityContextPersistenceFilter 这个Filter是整个拦截过程的入口和出口(也就是第一个和最后一个拦截器),会在请求开始时从配置好的 SecurityContextRepository 中获取 SecurityContext,然后把它设置给SecurityContextHolder。在请求完成后将SecurityContextHolder 持有的 SecurityContext 再保存到配置好的 SecurityContextRepository,同时清除 securityContextHolder 所持有的 SecurityContext;

UsernamePasswordAuthenticationFilter 用于处理来自表单提交的认证。该表单必须提供对应的用户名和密码,其内部还有登录成功或失败后进行处理的 AuthenticationSuccessHandler 和 AuthenticationFailureHandler,这些都可以根据需求做相关改变;

FilterSecurityInterceptor 是用于保护web资源的,使用AccessDecisionManager对当前用户进行授权访问;

ExceptionTranslationFilter 能够捕获来自 FilterChain 所有的异常,并进行处理。但是它只会处理两类异常:AuthenticationException 和 AccessDeniedException,其它的异常它会继续抛出。

三、SpringBoot+Security

Spring-boot-Security: 基于Spring Boot整合的快速实现。

1、项目搭建步骤

1、创建maven工程。

创建子模块spring-boot-security pom依赖:

  <?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <artifactId>AuthDemo</artifactId>
        <groupId>com.tuling</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <groupId>com.tuling</groupId>
    <artifactId>spring-boot-security</artifactId>
    <version>0.0.1</version>
    <name>spring-boot-security</name>
    <description>Demo project for Spring Boot</description>
​
    <properties>
        <java.version>1.8</java.version>
    </properties>
​
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
​
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
​
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
​
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
    </dependencies>
​
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>${spring-boot-version}</version>
            </plugin>
        </plugins>
    </build>
</project>

2、 在resources目录下创建application.properties。 --spring security不需要任何配置就可以直接启动

server.port=8080
spring.application.name=security-springboot

3、创建启动类,注意我们在启动类中,引入了一个Spring Security提供的注解@EnableWebSecurity。

package com.tuling.springbootsecurity;
​
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
​
@SpringBootApplication
@EnableWebSecurity
public class SpringBootSecurityApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringBootSecurityApplication.class, args);
    }
}

4、创建几个简单的资源访问接口

package com.tuling.springbootsecurity.controller;
​
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
​
@RestController
@RequestMapping("/mobile")
public class MobileController {
​
    @GetMapping("/query")
    public String query(){
        return "mobile";
    }
}
​
package com.tuling.springbootsecurity.controller;
​
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
​
@RestController
@RequestMapping("/salary")
public class SalaryController {
​
    @GetMapping("/query")
    public String query(){
        return "salary";
    }
}
​

这时访问这两个资源会转到一个登录页面,要求先登录。登录的用户名是user,密码会在控制台日志中打印。

2、用SpringBoot Security实现认证和授权逻辑。

5、注入免密解析器PasswordEncoder和用户来源UserDetailsService

package com.tuling.springbootsecurity.config;
​
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
​
@Configuration
public class MyWebConfig implements WebMvcConfigurer {
    //默认Url根路径跳转到/login,此url为spring security提供
    @Override
    public void addViewControllers(ViewControllerRegistry registry)
    {
        registry.addViewController("/").setViewName("redirect:/login");
    }
    /**
     * 自行注入一个PasswordEncoder。
     * @return
     */
    @Bean
    public PasswordEncoder getPassWordEncoder(){
        return new BCryptPasswordEncoder(10);
//        return NoOpPasswordEncoder.getInstance();
    }
​
    /**
     * 自行注入一个UserDetailsService
     * 如果没有的话,在UserDetailsServiceAutoConfiguration中会默认注入一个包含user用户的InMemoryUserDetailsManager
     * 另外也可以采用修改configure(AuthenticationManagerBuilder auth)方法并注入authenticationManagerBean的方式。
     * @return
     */
    @Bean
    public UserDetailsService userDetailsService(){
        InMemoryUserDetailsManager userDetailsManager = new InMemoryUserDetailsManager(User.withUsername("admin").password(passwordEncoder().encode("admin")).authorities("mobile","salary").build(),
                                                                                       User.withUsername("manager").password(passwordEncoder().encode("manager")).authorities("salary").build(),
                                                                                       User.withUsername("worker").password(passwordEncoder().encode("worker")).authorities("worker").build());
        return userDetailsManager;
//      return new JdbcUserDetailsManager(DataSource dataSource);
    }
}
​

6、注入校验配置规则:

package com.tuling.springbootsecurity.config;
​
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 MyWebSecurityConfig extends WebSecurityConfigurerAdapter {
​
    //配置安全拦截策略
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //链式配置拦截策略
        http.csrf().disable()//关闭csrg跨域检查
                .authorizeRequests()//打开请求认证授权
                .antMatchers("/mobile/**").hasAuthority("mobile") //配置资源权限
                .antMatchers("/salary/**").hasAuthority("salary")
                .antMatchers("/common/**").permitAll() //common下的请求直接通过
                .anyRequest().authenticated() //其他请求需要登录
                .and() //并行条件
                .formLogin().defaultSuccessUrl("/main.html").failureUrl("/common/loginFailed"); //可从默认的login页面登录,并且登录后跳转到main.html
    }
}
​

7、获取当前用户信息:Spring Security提供了多种获取当前用户信息的方法。

package com.tuling.springbootsecurity.controller;
​
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.User;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
​
import javax.servlet.http.HttpServletRequest;
import java.security.Principal;
​
@RestController
@RequestMapping("/common")
public class LoginController {
​
    @GetMapping("/getLoginUserByPrincipal")
    public String getLoginUserByPrincipal(Principal principal){
        return principal.getName();
    }
    @GetMapping(value = "/getLoginUserByAuthentication")
    public String currentUserName(Authentication authentication) {
        return authentication.getName();
    }
    @GetMapping(value = "/username")
    public String currentUserNameSimple(HttpServletRequest request) {
        Principal principal = request.getUserPrincipal();
        return principal.getName();
    }
    @GetMapping("/getLoginUser")
    public String getLoginUser(){
        User user = (User)SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        return user.getUsername();
    }
​
}
​

然后把前台页面移植过来。

这样,一个简单的Spring Secuity工程就配置完成了。

3、SpringBoot Security项目的扩展点

这样,一个基本的spring-boot-security项目就很快搭建起来了。而Spring Security实际上还提供了相当丰富的扩展点,包括用户名密码校验规则、资源校验规则、Session管理规则等。

1、主体数据来源

SpringSecurity通过引用Spring容器中的UserDetailsService对象来管理主体数据。默认情况下,会注入一个包含user用户的默认主体管理服务。演示中就通过注入一个InMemoryUserDetailsManager对象覆盖了默认的主体管理器。

实际项目中的用户信息大都会来自于数据库。在SpringSecurity中,也提供了JdbcUserDetailsManager来实现对数据库的用户信息进行管理。而如果这些不满足实际需求,可以通过自己实现一个UserDetailsService对象并注入到Spring容器中,来实现自定义的主体数据管理。

2、密码解析器

Spring Security提供了很多密码解析器,包括CryptPassEncoder、Argon2PasswordEncoder、Pbkdf2PasswordEncoder等,具体可以参看PassEncoder接口的实现类。其中最常用的一般就是BCryptPasswordEncoder。其中要注意的是,我们在选择不同的密码解析器后,后台存储用户密码时要存储对应的密文。

3、自定义授权及安全拦截策略

最常规的方式是通过覆盖WebSecurityConfigurerAdapter中的protected void configure(HttpSecurity http)方法。通过http来配置自定义的拦截规则。包含访问控制、登录页面及逻辑、退出页面及逻辑等。

自定义登录:http.loginPage()方法配置登录页,http.loginProcessingUrl()方法定制登录逻辑。要注意的是,SpringSecurity的登录页和登录逻辑是同一个地址/login,如果使用自定义的页面,需要将登录逻辑地址也分开。例如: http.loginPage("/index.html").loginProcessingUrl("/login")。

而登录页面的一些逻辑处理,可以参考系统提供的默认登录页。但是这里依然要注意登录页的访问权限。而关于登录页的源码,可以在DefaultLoginPageGeneratingFilter中找到。

记住我功能:登录页面提供了记住我功能,此功能只需要往登录时提交一个remeber-me的参数,值可以是 on 、yes 、1 、 true,就会记住当前登录用户的token到cookie中。http.rememberMe().rememberMeParameter("remeber-me"),使用这个配置可以定制参数名。而在登出时,会清除记住我功能的cookie。

拦截策略:antMachers()方法设置路径匹配,可以用两个星号代表多层路径,一个星号代表一个或多个字符,问号代表一个字符。然后配置对应的安全策略:

permitAll()所有人都可以访问。denyAll()所有人都不能访问。 anonymous()只有未登录的人可以访问,已经登录的无法访问。

hasAuthority、hasRole这些是配置需要有对应的权限或者角色才能访问。 其中,角色就是对应一个ROLE_角色名 这样的一个资源。

四、HttpSecurity配置项

方法说明
openidLogin()用于基于 OpenId 的验证
headers()将安全标头添加到响应
cors()配置跨域资源共享( CORS )
sessionManagement()允许配置会话管理
portMapper()向到 HTTPS 或者从 HTTPS 重定向到 HTTP。默认情况下,Spring Security使用一个PortMapperImpl映射 HTTP 端口8080到 HTTPS 端口8443,HTTP 端口80到 HTTPS 端口443
jee()配置基于容器的预认证。 在这种情况下,认证由Servlet容器管理
x509()配置基于x509的认证
rememberMe允许配置“记住我”的验证
authorizeRequests()允许基于使用HttpServletRequest限制访问
requestCache()允许配置请求缓存
exceptionHandling()允许配置错误处理
securityContext()在HttpServletRequests之间的SecurityContextHolder上设置SecurityContext的管理。 当使用WebSecurityConfifigurerAdapter时,这将
servletApi()将HttpServletRequest方法与在其上找到的值集成到SecurityContext中。 当使用WebSecurityConfifigurerAdapter时,这将自动应用
csrf()添加 CSRF 支持,使用WebSecurityConfifigurerAdapter时,默认启用
logout()添加退出登录支持。当使用WebSecurityConfifigurerAdapter时,这将自动应用。默认情况是,访问URL”/ logout”,使HTTP Session无效
anonymous()允许配置匿名用户的表示方法。 当与WebSecurityConfifigurerAdapter结合使用时,这将自动应用。 默认情况下,匿名用户将使用
formLogin()指定支持基于表单的身份验证。如果未指定FormLoginConfifigurer#loginPage(String),则将生成默认登录页面
oauth2Login()根据外部OAuth 2.0或OpenID Connect 1.0提供程序配置身份验证
requiresChannel()配置通道安全。为了使该配置有用,必须提供至少一个到所需信道的映射
httpBasic()配置 Http Basic 验证
addFilterAt()允许配置错误处理
exceptionHandling()在指定的Filter类的位置添加过滤器
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Security 是一个基于 Spring 框架的安全性框架,它提供了一系列的安全性服务,包括认证、授权、攻击防范等。使用 Spring Security 可以帮助开发者更加方便地实现 Web 应用程序的安全性控制。下面是一个使用 Spring Security 的例子: @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private UserDetailsService userDetailsService; @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .antMatchers("/admin/**").hasRole("ADMIN") .antMatchers("/user/**").hasAnyRole("USER", "ADMIN") .anyRequest().authenticated() .and() .formLogin() .loginPage("/login") .permitAll() .and() .logout() .permitAll(); } @Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService); } } 在上面的例子中,我们定义了一个 SecurityConfig 类,它继承了 WebSecurityConfigurerAdapter 类,并使用了 @EnableWebSecurity 注解来启用 Spring Security。在 configure() 方法中,我们定义了一些安全性规则,比如只有拥有 ADMIN 角色的用户才能访问 /admin/** 的 URL,而拥有 USER 或 ADMIN 角色的用户才能访问 /user/** 的 URL。在 configureGlobal() 方法中,我们使用了一个 UserDetailsService 来获取用户的认证信息。这个例子只是 Spring Security 的一个简单使用示例,实际上 Spring Security 还提供了很多其它的功能和选项,可以根据具体的需求进行配置和使用

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值