Spring Security OAuth2.0认证授权(一)

目录

1、基本概念

1、认证

2、会话

 3、授权

 4、RBAC

1、基于角色的访问控制

2、基于资源的访问控制

2、基于Session的认证方式

3、Spring Security的使用(xml方式)

4、Spring Security的使用

5、Spring Security应用

1、SpringBoot集成

2、工作原理

3、认证和授权

1、认证流程

2、鉴权流程

3、自定义页面实现

4、会话

1、会话控制

2、会话超时

3、安全会话cookie

4、退出

5、授权

使用注解


1、基本概念

1、认证

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

认证是为了保护系统的隐私数据与资源,用户的身份合法方可访问该系统的资源。

2、会话

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

  • 基于session的认证方式如下图:

它的交互流程是,用户认证成功后,在服务端生成用户相关的数据保存在session(当前会话)中,发给客户端的sesssion_id 存放到 cookie 中,这样用户客户端请求时带上 session_id 就可以验证服务器端是否存在 session 数据,以此完成用户的合法校验,当用户退出系统或session过期销毁时,客户端的session_id也就无效了。

Spring Security OAuth2.0认证授权(一)_第1张图片

  •  基于token方式如下图:

它的交互流程是,用户认证成功后,服务端生成一个token发给客户端,客户端可以放到 cookie 或 localStorage等存储中,每次请求时带上 token,服务端收到token通过验证后即可确认用户身份。

Spring Security OAuth2.0认证授权(一)_第2张图片

 3、授权

授权是用户认证通过根据用户的权限来控制用户访问资源的过程,拥有资源的访问权限则正常访问,没有权限则拒绝访问。

Spring Security OAuth2.0认证授权(一)_第3张图片

 4、RBAC

如何实现授权?业界通常基于RBAC实现授权。

1、基于角色的访问控制

RBAC基于角色的访问控制(Role-Based Access Control)是按角色进行授权,比如:主体的角色为总经理可以查询企业运营报表,查询员工工资信息等,访问控制流程如下:

Spring Security OAuth2.0认证授权(一)_第4张图片Spring Security OAuth2.0认证授权(一)_第5张图片

如果上图中查询工资所需要的角色变化为总经理和部门经理,此时就需要修改判断逻辑为“判断用户的角色是否是总经理或部门经理”,修改代码如下: 

 

当需要修改角色的权限时就需要修改授权的相关代码,系统可扩展性差。 

2、基于资源的访问控制

Spring Security OAuth2.0认证授权(一)_第6张图片Spring Security OAuth2.0认证授权(一)_第7张图片

系统设计时定义好查询工资的权限标识,即使查询工资所需要的角色变化为总经理和部门经理也不需要修改授权代码,系统可扩展性强。 

2、基于Session的认证方式

基于Session认证方式的流程是,用户认证成功后,在服务端生成用户相关的数据保存在session(当前会话),而发给客户端的 sesssion_id 存放到 cookie 中,这样用客户端请求时带上 session_id 就可以验证服务器端是否存在session 数据,以此完成用户的合法校验。当用户退出系统或session过期销毁时,客户端的session_id也就无效了。

基于Session的认证机制由Servlet规范定制,Servlet容器已实现,用户通过HttpSession的操作方法即可实现,如下是HttpSession相关的操作API。

Spring Security OAuth2.0认证授权(一)_第8张图片

  
    
      org.springframework
      spring-webmvc
      5.1.5.RELEASE
    
    
      javax.servlet
      javax.servlet-api
      3.0.1
      provided
    
    
      org.projectlombok
      lombok
      1.18.20
    
  

  
    
      
        
          org.apache.tomcat.maven
          tomcat7-maven-plugin
          2.2

        
        
          org.apache.maven.plugins
          maven-compiler-plugin
          
            1.8
            1.8
          
        
      
    
  

本案例采用Servlet3.0无web.xml方式。

Spring Security OAuth2.0认证授权(一)_第9张图片

Spring容器配置

@Configuration //相当于applicationContext.xml
@ComponentScan(basePackages = "com.security"
        ,excludeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION,value = Controller.class)})
public class ApplicationConfig {
    //在此配置除了Controller的其它bean,比如:数据库链接池、事务管理器、业务bean等。
}

Spring Context配置 

@Configuration //相当于springmvc.xml
@EnableWebMvc
@ComponentScan(basePackages = "com.security"
        ,includeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION,value = Controller.class)})
public class WebConfig implements WebMvcConfigurer {

    @Autowired
    private MyInterceptor myInterceptor;

    //视图解析器
    @Bean
    public InternalResourceViewResolver viewResolver(){
        InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
        viewResolver.setPrefix("/WEB-INF/");
        viewResolver.setSuffix(".jsp");
        return viewResolver;
    }

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/").setViewName("login");
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(myInterceptor).addPathPatterns("/r*");
    }
}

 在init包下定义Spring容器初始化类SpringApplicationInitializer,此类实现WebApplicationInitializer接口,Spring容器启动时加载WebApplicationInitializer接口的所有实现类。——加载Spring容器

//相当于web.xml
public class SpringApplicationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

    //spring容器,applicationContext.xml
    @Override
    protected Class[] getRootConfigClasses() {
        return new Class[]{ApplicationConfig.class};
    }

    //servletContext,springmvc.xml
    @Override
    protected Class[] getServletConfigClasses() {
        return new Class[]{WebConfig.class};
    }

    //url-mapping
    @Override
    protected String[] getServletMappings() {
        return new String[]{"/"};
    }
}

Spring Security OAuth2.0认证授权(一)_第10张图片

@RestController
public class UserController {

    @Autowired
    private UserService userService;

    @RequestMapping(value = "/login",produces = {"text/plain;charset=UTF-8"})
    public String login(UserRequest userRequest, HttpSession httpSession){
        UserDto userDto = userService.login(userRequest);

        httpSession.setAttribute("_user",userDto);
        return userDto.getUsername()+"登录成功";
    }

    @RequestMapping(value = "/r1",produces = {"text/plain;charset=UTF-8"})
    public String r1(HttpSession session){
        String result = "";
        Object user = session.getAttribute("_user");
        if(user == null){
            result = "匿名访问";
        }else{
            UserDto userDto = (UserDto) user;
            result = userDto.getFullname()+"欢迎访问----r1";
        }
        return result;
    }

    @RequestMapping(value = "/r2",produces = {"text/plain;charset=UTF-8"})
    public String rr2(HttpSession session){
        String result = "";
        Object user = session.getAttribute("_user");
        if(user == null){
            result = "匿名访问";
        }else{
            UserDto userDto = (UserDto) user;
            result = userDto.getFullname()+"欢迎访问----r2";
        }
        return result;
    }

    @RequestMapping(value = "/logout",produces = {"text/plain;charset=UTF-8"})
    public String logout(HttpSession session){
        session.invalidate();
        return "退出成功";
    }


}
@Service
public class UserServiceImpl implements UserService {

    @Override
    public UserDto login(UserRequest userRequest) {
        if(userRequest == null || StringUtils.isEmpty(userRequest.getPassword())
                || StringUtils.isEmpty(userRequest.getUsername())){
            throw new RuntimeException("账号或密码为空");
        }
        UserDto user = getUser(userRequest.getUsername());
        if(user == null){
            throw new RuntimeException("没有此用户");
        }
        if(!user.getPassword().equals(userRequest.getPassword())){
            throw new RuntimeException("用户或密码错误");
        }
        return user;
    }

    private UserDto getUser(String username){
        Map map = new HashMap<>();

        Set a1 = new HashSet<>();
        a1.add("r1");
        Set a2 = new HashSet<>();
        a2.add("r2");


        map.put("aa",new UserDto(1,"aa","123","151525","张三",a1));
        map.put("bb",new UserDto(2,"bb","321","369517","李四",a2));
        return map.get(username);
    }

}
@Component
public class MyInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        Object user = request.getSession().getAttribute("_user");
        response.setContentType("text/html;charset=utf-8");
        if(user == null){
            response.getWriter().println("请登录");
            return false;
        }
        UserDto userDto = (UserDto) user;

        String requestURL = request.getRequestURL().toString();
        if(userDto.getAuthorities().contains("r1") && requestURL.contains("/r1")){
            return true;
        }
        if(userDto.getAuthorities().contains("r2") && requestURL.contains("/r2")){
            return true;
        }
        response.getWriter().println("你没有权限");
        return false;
    }
}

3、Spring Security的使用(xml方式)

Spring Security基本使用_但盼风雨来 能留你在此的博客-CSDN博客

4、Spring Security的使用

加上

    
      org.springframework.security
      spring-security-web
      5.0.5.RELEASE
    
    
      org.springframework.security
      spring-security-config
      5.0.5.RELEASE
    

spring security提供了用户名密码登录、退出、会话管理等认证功能,只需要配置即可使用。

Spring Security OAuth2.0认证授权(一)_第11张图片

  • (1)在config包下定义WebSecurityConfig,安全配置的内容包括:用户信息、密码编码器、安全拦截机制。
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    //定义用户信息服务(查询用户信息)
    @Bean
    public UserDetailsService userDetailsService(){
        //内存方式定义用户信息
        InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
        manager.createUser(User.withUsername("zhangsan").password("123").authorities("p1").build());
        manager.createUser(User.withUsername("lisi").password("321").authorities("p1").build());
        return manager;
    }

    //密码编码器
    @Bean
    public PasswordEncoder passwordEncoder(){
        //无加密方式
        return NoOpPasswordEncoder.getInstance();
    }

    //安全拦截机制
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable().authorizeRequests().
                antMatchers("/r/r1").hasAnyAuthority("p1").//访问/r/r1必须具有p1权限
                antMatchers("/r/r2").hasAnyAuthority("p2").
                antMatchers("/r/*").authenticated().//所以/r*请求必须认证
                anyRequest().permitAll().//其他请求直接通过
                and().
                formLogin().//允许表单登录
                successForwardUrl("/login-success");//自定义登录成功页面地址
    }
}
@Configuration //相当于springmvc.xml
@EnableWebMvc
@ComponentScan(basePackages = "com.security"
        ,includeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION,value = Controller.class)})
public class WebConfig implements WebMvcConfigurer {


    //视图解析器
    @Bean
    public InternalResourceViewResolver viewResolver(){
        InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
        viewResolver.setPrefix("/WEB-INF/");
        viewResolver.setSuffix(".jsp");
        return viewResolver;
    }

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        //使用security自己的 login 页面
        registry.addRedirectViewController("/","/login");
    }

}

加载 WebSecurityConfig 

//相当于web.xml
public class SpringApplicationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

    //spring容器,applicationContext.xml
    @Override
    protected Class[] getRootConfigClasses() {
        return new Class[]{ApplicationConfig.class, WebSecurityConfig.class};
    }

    //servletContext,springmvc.xml
    @Override
    protected Class[] getServletConfigClasses() {
        return new Class[]{WebConfig.class};
    }

    //url-mapping
    @Override
    protected String[] getServletMappings() {
        return new String[]{"/"};
    }
}

Spring Security初始化,这里有两种情况:

  • 若当前环境没有使用 Spring或Spring MVC,则需要将 WebSecurityConfig(Spring Security配置类) 传入超类,以确保获取配置,并创建spring context。
  • 相反,若当前环境已经使用 spring,我们应该在现有的springContext中注册Spring Security(上一步已经做将WebSecurityConfig加载至rootcontext),此方法可以什么都不做。

在init包下定义SpringSecurityApplicationInitializer:

public class SpringSecurityApplicationInitializer extends AbstractSecurityWebApplicationInitializer {

//    public SpringSecurityApplicationInitializer() {
//        super(WebSecurityConfig.class);
//    }
}
@RestController
public class UserController {

    @RequestMapping(value = "/login-success",produces = {"text/plain;charset=UTF-8"})
    public String logout(HttpSession session){
        session.invalidate();
        return "登录成功";
    }

    @RequestMapping(value = "/r/r1",produces = {"text/plain;charset=UTF-8"})
    public String r1(){

        return "访问资源1";
    }


    @RequestMapping(value = "/r/r2",produces = {"text/plain;charset=UTF-8"})
    public String r2(){

        return "访问资源2";
    }
}

5、Spring Security应用

1、SpringBoot集成

Spring Security OAuth2.0认证授权(一)_第12张图片

    
        
            org.springframework.boot
            spring-boot-starter-web
        

        
            org.springframework.boot
            spring-boot-starter-security
        

        
        
            javax.servlet
            javax.servlet-api
            provided
        
        
        
            javax.servlet
            jstl
        
        
            org.springframework.boot
            spring-boot-starter-tomcat
            provided
        
        
        
            org.apache.tomcat.embed
            tomcat-embed-jasper
        

        
            org.projectlombok
            lombok
            1.18.20
        

        
            org.springframework.boot
            spring-boot-starter-test
            test
        
    

    
        
            
                
                    org.apache.tomcat.maven
                    tomcat7-maven-plugin
                    2.2

                
                
                    org.apache.maven.plugins
                    maven-compiler-plugin
                    
                        1.8
                        1.8
                    
                
            
        
    
spring.mvc.view.prefix=/
spring.mvc.view.suffix=.jsp

server.port=8080
server.servlet.context-path=/springboot-security
spring.application.name=springboot-security
@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        //使用security自己的 login 页面
        registry.addRedirectViewController("/","/login");
    }

}
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    //定义用户信息服务(查询用户信息)
    @Bean
    public UserDetailsService userDetailsService(){
        //内存方式定义用户信息
        InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
        manager.createUser(User.withUsername("zhangsan").password("123").authorities("p1").build());
        manager.createUser(User.withUsername("lisi").password("321").authorities("p1").build());
        return manager;
    }

    //密码编码器
    @Bean
    public PasswordEncoder passwordEncoder(){
        //无加密方式
        return NoOpPasswordEncoder.getInstance();
    }

    //安全拦截机制
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable().authorizeRequests().
                antMatchers("/r/r1").hasAnyAuthority("p1").//访问/r/r1必须具有p1权限
                antMatchers("/r/r2").hasAnyAuthority("p2").
                antMatchers("/r/*").authenticated().//所以/r*请求必须认证
                anyRequest().permitAll().//其他请求直接通过
                and().
                formLogin().//允许表单登录
                successForwardUrl("/login-success");//自定义登录成功页面地址
    }
}

2、工作原理

Spring Security所解决的问题就是安全访问控制,而安全访问控制功能其实就是对所有进入系统的请求进行拦截,校验每个请求是否能够访问它所期望的资源。根据前边知识的学习,可以通过Filter或AOP等技术来实现,SpringSecurity对Web资源的保护是靠Filter实现的,所以从这个Filter来入手,逐步深入Spring Security原理。

当初始化Spring Security时,会创建一个名为 SpringSecurityFilterChain 的Servlet过滤器,类型为org.springframework.security.web.FilterChainProxy,它实现了javax.servlet.Filter,因此外部的请求会经过此类,下图是Spring Security过虑器链结构图:

Spring Security OAuth2.0认证授权(一)_第13张图片Spring Security OAuth2.0认证授权(一)_第14张图片

 FilterChainProxy 是一个代理,真正起作用的是FilterChainProxy中SecurityFilterChain所包含的各个Filter,同时这些Filter作为Bean被Spring管理,它们是Spring Security核心,各有各的职责,但他们并不直接处理用户的认证,也不直接处理用户的授权,而是把它们交给了认证管理器(AuthenticationManager)和决策管理器
(AccessDecisionManager)进行处理,上图是FilterChainProxy相关类的UML图示。spring Security功能的实现主要是由一系列过滤器链相互配合完成。

Spring Security OAuth2.0认证授权(一)_第15张图片

  •  SecurityContextPersistenceFilter 这个Filter是整个拦截过程的入口和出口(也就是第一个和最后一个拦截器),会在请求开始时从配置好的 SecurityContextRepository 中获取 SecurityContext,然后把它设置给SecurityContextHolder。在请求完成后将 SecurityContextHolder 持有的 SecurityContext 再保存到配置好的 SecurityContextRepository,同时清除 securityContextHolder 所持有的 SecurityContext;
  • UsernamePasswordAuthenticationFilter 用于处理来自表单提交的认证。该表单必须提供对应的用户名和密码,其内部还有登录成功或失败后进行处理的 AuthenticationSuccessHandler 和AuthenticationFailureHandler,这些都可以根据需求做相关改变;
  • FilterSecurityInterceptor 是用于保护web资源的,使用AccessDecisionManager对当前用户进行授权访问,前面已经详细介绍过了;
  • ExceptionTranslationFilter 能够捕获来自 FilterChain 所有的异常,并进行处理。但是它只会处理两类异常:AuthenticationException 和 AccessDeniedException,其它的异常它会继续抛出。

3、认证和授权

1、认证流程

Spring Security OAuth2.0认证授权(一)_第16张图片

让我们仔细分析认证过程:

  1. 用户提交用户名、密码被SecurityFilterChain中的 UsernamePasswordAuthenticationFilter 过滤器获取到,封装为请求Authentication,通常情况下是UsernamePasswordAuthenticationToken这个实现类。
  2. 然后过滤器将Authentication提交至认证管理器(AuthenticationManager)进行认证
  3. 认证成功后, AuthenticationManager 身份管理器返回一个被填充满了信息的(包括上面提到的权限信息,身份信息,细节信息,但密码通常会被移除) Authentication 实例。
  4.  SecurityContextHolder 安全上下文容器将第3步填充了信息的 Authentication ,通过SecurityContextHolder.getContext().setAuthentication(…)方法,设置到其中。可以看出AuthenticationManager接口(认证管理器)是认证相关的核心接口,也是发起认证的出发点,它的实现类为ProviderManager。而Spring Security支持多种认证方式,因此ProviderManager维护着一个List 列表,存放多种认证方式,最终实际的认证工作是由AuthenticationProvider完成的。咱们知道web表单的对应的AuthenticationProvider实现类为DaoAuthenticationProvider,它的内部又维护着一个UserDetailsService负责UserDetails的获取。最AuthenticationProvider将UserDetails填充至Authentication。

2、鉴权流程

Spring Security可以通过 http.authorizeRequests() 对web请求进行授权保护。SpringSecurity使用标准Filter建立了对web请求的拦截,最终实现对资源的授权访问。

 Spring Security OAuth2.0认证授权(一)_第17张图片

  • 拦截请求,已认证用户访问受保护的web资源将被SecurityFilterChain中的 FilterSecurityInterceptor 的子类拦截。
  • 获取资源访问策略,FilterSecurityInterceptor会从 SecurityMetadataSource 的子类DefaultFilterInvocationSecurityMetadataSource 获取要访问当前资源所需要的权限Collection 。SecurityMetadataSource其实就是读取访问策略的抽象,而读取的内容,其实就是我们配置的访问规则, 读取访问策略如:

Spring Security OAuth2.0认证授权(一)_第18张图片

  • 最后,FilterSecurityInterceptor会调用 AccessDecisionManager 进行授权决策,若决策通过,则允许访问资源,否则将禁止访问。AccessDecisionManager(访问决策管理器)的核心接口如下: 

Spring Security OAuth2.0认证授权(一)_第19张图片

decide的参数:

  • authentication:要访问资源的访问者的身份
  • object:要访问的受保护资源,web请求对应FilterInvocation
  • configAttributes:是受保护资源的访问策略,通过SecurityMetadataSource获取。

decide接口就是用来鉴定当前用户是否有访问对应受保护资源的权限。

AccessDecisionManager采用投票的方式来确定是否能够访问受保护资源。

Spring Security OAuth2.0认证授权(一)_第20张图片

3、自定义页面实现

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        //使用security自己的 login 页面
        registry.addViewController("/").setViewName("redirect:/login-view");
        registry.addViewController("/login-view").setViewName("login");
    }

}
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    //定义用户信息服务(查询用户信息)
    @Bean
    public UserDetailsService userDetailsService(){
        //内存方式定义用户信息
        InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
        manager.createUser(User.withUsername("zhangsan").password("123").authorities("p1").build());
        manager.createUser(User.withUsername("lisi").password("321").authorities("p1").build());
        return manager;
    }

    //密码编码器
    @Bean
    public PasswordEncoder passwordEncoder(){
        //无加密方式
        return NoOpPasswordEncoder.getInstance();
    }

    //安全拦截机制
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable().//屏蔽CSRF,使security 不再限制CSRF
                authorizeRequests().
                antMatchers("/r/r1").hasAnyAuthority("p1").//访问/r/r1必须具有p1权限
                antMatchers("/r/r2").hasAnyAuthority("p2").
                antMatchers("/r/*").authenticated().//所以/r*请求必须认证
                anyRequest().permitAll().//其他请求直接通过
                and().
                formLogin().//允许表单登录
                loginPage("/login-view").//登录页面
                loginProcessingUrl("/login").
                successForwardUrl("/login-success").//自定义登录成功页面地址
                permitAll();
    }
}

4、会话

用户认证通过后,为了避免用户的每次操作都进行认证可将用户的信息保存在会话中。spring security提供会话管理,认证通过后将身份信息放入SecurityContextHolder上下文,SecurityContext与当前线程进行绑定,方便获取用户身份。

从数据库中获取用户

@Service
public class MyUserDetailService implements UserDetailsService {

    @Autowired
    private UserDao userDao;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userDao.getUser(username);
        if(user == null){
            //如果查不到,返回null,由provider抛异常
            return null;
        }
        return org.springframework.security.core.userdetails.User.
                withUsername(user.getUsername()).
                password(user.getPassword()).
                authorities("p1").build();
    }
}

获取登录用户信息

    @RequestMapping(value = "/get",produces = {"text/plain;charset=UTF-8"})
    public String getUserName(){
        Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        if(principal == null){
            return "匿名";
        }

        if(principal instanceof UserDetails){
            UserDetails user = (UserDetails) principal;
            return "你好,"+user.getUsername();
        }else{
            return principal.toString();
        }
    }

1、会话控制

我们可以通过以下选项准确控制会话何时创建以及Spring Security如何与之交互:

Spring Security OAuth2.0认证授权(一)_第21张图片

 Spring Security OAuth2.0认证授权(一)_第22张图片

默认情况下,Spring Security会为每个登录成功的用户会新建一个Session,就是 ifRequired

若选用never,则指示Spring Security对登录成功的用户不创建Session了,但若你的应用程序在某地方新建了session,那么Spring Security会用它的。

若使用stateless,则说明Spring Security对登录成功的用户不会创建Session了,你的应用程序也不会允许新建session。并且它会暗示不使用cookie,所以每个请求都需要重新进行身份验证。这种无状态架构适用于REST API及其无状态认证机制。 

2、会话超时

可以再sevlet容器中设置Session的超时时间,如下设置Session有效期为3600s;

server.servlet.session.timeout=3600s

session超时之后,可以通过Spring Security 设置跳转的路径。

http.sessionManagement()
    .expiredUrl("/login‐view?error=EXPIRED_SESSION")
    .invalidSessionUrl("/login‐view?error=INVALID_SESSION");

expired指session过期,invalidSession指传入的sessionid无效。

3、安全会话cookie

我们可以使用httpOnly和secure标签来保护我们的会话cookie:

  • httpOnly :如果为true,那么浏览器脚本将无法访问cookie
  • secure :如果为true,则cookie将仅通过HTTPS连接发送
server.servlet.session.cookie.http‐only=true
server.servlet.session.cookie.secure=true

4、退出

Spring Security OAuth2.0认证授权(一)_第23张图片

当退出操作出发时,将发生:

  • 使 HTTP Session 无效
  • 清除 SecurityContextHolder
  • 跳转到 /login -view?logout

Spring Security OAuth2.0认证授权(一)_第24张图片

  1. 提供系统退出支持,使用 WebSecurityConfigurerAdapter 会自动被应用
  2. 设置触发退出操作的URL (默认是 /logout ).
  3. 退出之后跳转的URL。默认是 /login?logout 。
  4. 定制的  LogoutSuccessHandler ,用于实现用户退出成功时的处理。如果指定了这个选项那么logoutSuccessUrl() 的设置会被忽略
  5. 添加一个 LogoutHandler ,用于实现用户退出时的清理工作。默认 SecurityContextLogoutHandler 会被添加为最后一个 LogoutHandler 。
  6. 指定是否在退出时让 HttpSession 无效。 默认设置为 true。

注意:如果让logout在GET请求下生效,必须关闭防止CSRF攻击csrf().disable()。如果开启了CSRF,必须使用post方式请求/logout。

logoutHandler:

一般来说, LogoutHandler 的实现类被用来执行必要的清理,因而他们不应该抛出异常。

下面是Spring Security提供的一些实现:

  • PersistentTokenBasedRememberMeServices 基于持久化token的RememberMe功能的相关清理
  • TokenBasedRememberMeService  基于token的RememberMe功能的相关清理
  • CookieClearingLogoutHandler 退出时Cookie的相关清理
  • CsrfLogoutHandler 负责在退出时移除csrfToken
  • SecurityContextLogoutHandler 退出时SecurityContext的相关清理

链式API提供了调用相应的 LogoutHandler 实现的快捷方式,比如deleteCookies()。

5、授权

授权的方式包括 web授权方法授权,web授权是通过 url拦截进行授权,方法授权是通过 方法拦截进行授权。他们都会调用accessDecisionManager进行授权决策,若为web授权则拦截器为FilterSecurityInterceptor;若为方法授权则拦截器为MethodSecurityInterceptor。如果同时通过web授权和方法授权则先执行web授权,再执行方法授权,最后决策通过,则允许访问资源,否则将禁止访问。

Spring Security OAuth2.0认证授权(一)_第25张图片

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userDao.getUser(username);
        if(user == null){
            //如果查不到,返回null,由provider抛异常
            return null;
        }

        List list = userDao.getPermission(user.getId());
        String[] arr = new String[list.size()];
        list.toArray(arr);

        return org.springframework.security.core.userdetails.User.
                withUsername(user.getUsername()).
                password(user.getPassword()).
                authorities(arr).build();
    }

Spring Security OAuth2.0认证授权(一)_第26张图片

  1. http.authorizeRequests() 方法有多个子节点,每个macher按照他们的声明顺序执行。
  2. 指定"/r/r1"URL,拥有p1权限能够访问
  3. 指定"/r/r2"URL,拥有p2权限能够访问
  4. 指定了"/r/r3"URL,同时拥有p1和p2权限才能够访问
  5. 指定了除了r1、r2、r3之外"/r/**"资源,同时通过身份认证就能够访问,这里使用SpEL(Spring ExpressionLanguage)表达式。。
  6. 剩余的尚未匹配的资源,不做保护。

注意:

规则的顺序是重要的,更具体的规则应该先写.现在以/ admin开始的所有内容都需要具有ADMIN角色的身份验证用户,即使是/ admin / login路径(因为/ admin / login已经被/ admin / **规则匹配,因此第二个规则被忽略).

.antMatchers("/admin/**").hasRole("ADMIN") 
.antMatchers("/admin/login").permitAll()

因此,登录页面的规则应该在/ admin / **规则之前.例如.

.antMatchers("/admin/login").permitAll() 
.antMatchers("/admin/**").hasRole("ADMIN")

保护URL常用的方法有:

  • authenticated() 保护URL,需要用户登录
  • permitAll() 指定URL无需保护,一般应用与静态资源文件
  • hasRole(String role) 限制单个角色访问,角色将被增加 “ROLE_” .所以”ADMIN” 将和 “ROLE_ADMIN”进行比较.
  • hasAuthority(String authority) 限制单个权限访问
  • hasAnyRole(String… roles)允许多个角色访问.
  • hasAnyAuthority(String… authorities) 允许多个权限访问.
  • access(String attribute) 该方法使用 SpEL表达式, 所以可以创建复杂的限制.
  • hasIpAddress(String ipaddressExpression) 限制IP地址或子网 

使用注解

从Spring Security2.0版本开始,它支持服务层方法的安全性的支持。

我们可以在任何 @Configuration 实例上使用 @EnableGlobalMethodSecurity 注释来启用基于注解的安全性。

@PreAuthorize,@PostAuthorize, @Secured三类注解。

public interface BankService {
    @Secured("IS_AUTHENTICATED_ANONYMOUSLY")
    public Account readAccount(Long id);

    @Secured("IS_AUTHENTICATED_ANONYMOUSLY")
    public Account[] findAccounts();

    @Secured("ROLE_TELLER")
    public Account post(Account account, double amount);
}

以上配置标明readAccount、findAccounts方法可匿名访问,底层使用WebExpressionVoter投票器,post方法需要有TELLER角色才能访问,底层使用RoleVoter投票器。 

ublic interface BankService {
    @PreAuthorize("isAnonymous()")
    public Account readAccount(Long id);

    @PreAuthorize("isAnonymous()")
    public Account[] findAccounts();

    @PreAuthorize("hasAuthority('p_transfer') and hasAuthority('p_read_account')")
    public Account post(Account account, double amount);
}

以上配置标明readAccount、findAccounts方法可匿名访问,post方法需要同时拥有p_transfer和p_read_account权限才能访问,底层使用WebExpressionVoter投票器,  

你可能感兴趣的:(其他技术,spring,security)