0


Spring Security认证授权-权限验证使用教程(一)

1.Security 基本配置

加入如下依赖

<!--Security-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

创建一个测试Controller如下

启动项目后浏览器访问 http://localhost:8080/hello

提示登录

**application.yml **配置 security的 **name **与 password

登录后即可正常访问

2.配置不同用户访问页面的不同权限

管理员,同事具有 ADMIN,USER权限,可以访问所有资源

普通用户,只能访问 /product/**

@Override
protected  void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
    // 管理员,同事具有 ADMIN,USER权限,可以访问所有资源
    .withUser("admin1").password(new BCryptPasswordEncoder().encode("admin1")).roles("ADMIN","USER") 
    .and().passwordEncoder(new BCryptPasswordEncoder())
    //  普通用户,只能访问 /product/**
    .withUser("user1").password(new BCryptPasswordEncoder().encode("user1")).roles("USER");  
}

3.添加管理员以及普通用户

管理员:ADMIN

用户:USER

@Override
protected void configure(HttpSecurity httpSecurity) throws Exception{
    httpSecurity.authorizeRequests().antMatchers("/product/**").hasAnyRole("USER")
      .antMatchers("/admin/**").hasAnyRole("ADMIN")
      .anyRequest().authenticated().and().formLogin().and().httpBasic();
}

4.获取当前登录用户信息

通过SecurityContextHolder来获取用户信息

@RequestMapping("/info")
@ResponseBody
public String product(){
    //控制器获取当前登录用户
    String currentUser = "";
    Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
    if(principal instanceof UserDetails){
        currentUser = ((UserDetails) principal).getUsername();
    }else {
        currentUser = principal.toString();
    }
    return "配太华以为刀兮、长四读以为佩. ----"+currentUser;
}

5.Security 核心组件介绍

spring security核心组件有:

SecurityContext、SecurityContextHolder、Authentication、Userdetails 和 AuthenticationManager

5.1SecurityContext

安全上下文,用户通过Spring Security 的校验之后,验证信息存储在SecurityContext中,SecurityContext的接口定义如下:

定义了两个方法,实际上其主要作用就是获取Authentication对象

5.2SecurityContextHolder

是一个持有者,用来持有住SecurityContext实例的

在Spring Security中,在请求之间存储SecurityContext的责任落在SecurityContextPersistenceFilter上,默认情况下,该上下文将上下文存储为HTTP请求之间的HttpSession属性。它会为每个请求恢复上下文SecurityContextHolder,并且最重要的是,在请求完成时清除SecurityContextHolder。SecurityContextHolder是一个类,他的功能方法都是静态的(static)。

SecurityContextHolder可以设置指定JVM策略(SecurityContext的存储策略)

这个策略有三种:

MODE_THREADLOCAL:SecurityContext 存储在线程中。

MODE_INHERITABLETHREADLOCAL:SecurityContext 存储在线程中,但子线程可以获取到父线程中的 SecurityContext。

MODE_GLOBAL:SecurityContext 在所有线程中都相同。

SecurityContextHolder默认使用MODE_THREADLOCAL模式,即存储在当前线程中。

//其作用就是存储当前认证信息
SecurityContextHolder.getContext().setAuthentication(token);

5.3Authentication(直译,认证)

一组用户名密码信息.

该接口有四个get方法,分别表示

  • Authorities:填充的是用户角色信息。
  • Credentials:填充的是密码。
  • Details:用户信息。
  • Principal:填充的是用户名。
/*
 *获取用户权限,一般情况下获取到的是用户的角色信息。
 */
getAuthorities:

/*
 *获取证明用户认证的信息,通常情况下获取到的是密码等信息。
 */
getCredentials:

/*
 *获取用户的额外信息,(这部分信息可以是我们的用户表中的信息)。
 */
getDetails:

/*
 *获取用户身份信息,在未认证的情况下获取到的是用户名,在已认证的情况下获取到的是
 * UserDetails (UserDetails也是一个接口,里边的方法有getUsername,getPassword等)。
 */
getPrincipal:

/*
 *获取当前 Authentication 是否已认证。
 */
isAuthenticated:

/*
 *设置当前 Authentication 是否已认证(true or false)。
 */
setAuthenticated:

5.4UserDetails

存储用户信息

方法含义如下

/*
 *获取用户权限,本质上是用户的角色信息。
 */
 getAuthorites
 
 /*
 *获取密码。
 */
 getPassword
 
 /*
 *获取用户名。
 */
 getUserName
 
 /*
 *账户是否过期。
 */
 isAccountNonExpired
 
 /*
 *账户是否被锁定。
 */
 isAccountNonLocked
 
 /*
 *密码是否过期。
 */
 isCredentialsNonExpired
 
 /*
 *账户是否可用。
 */
 isEnabled

5.5UserDetailsService

UserDetailsService也是一个接口,且只有一个方法loadUserByUsername,他可以用来获取UserDetails。

通常会定义一个CustomUserDetailsService来实现UserDetailsService接口,并实现其loadUserByUsername方法。在实现loadUserByUsername方法的时候,可以通过查询数据库(或者是缓存、或者是其他的存储形式)来获取用户信息,然后组装成一个UserDetails,并返回。

5.6AuthenticationManager

AuthenticationManager是一个接口,它只有一个方法,接收参数为Authentication

AuthenticationManager 的作用就是校验Authentication,

如果验证失败会抛出AuthenticationException异常。

6.Spring Security的一些工作原理

6.1spring security 在web应用中是基于filter的

Spring SecurityFilter 中创建 Authentication 对象,并调用 AuthenticationManager 进行校验.

spring security 维护了一个filter chain

chain中的每一个filter都具有特定的责任,并根据所需的服务在配置总添加。

filter的顺序很重要,因为他们之间存在依赖关系。

pring security中有如下filter(按顺序的):

1.ChannelProcessingFilter:因为它可能需要重定向到不同的协议
2.SecurityContextPersistenceFilter:可以在web请求开头的SecurityContextHolder中设置SecurityContext:并且SecurityContext的任何更改都可以复制到HttpSession当web请求结束时准备好与下一个web请求一起使用
3.ConcurrentSessionFilter:身份验证处理-UsernamePasswordAuthenticationFilter,CasAuthenticationFilter,BasicAuthenticationFilter等。以便SecurityContextHolder可以修改为包含有效的Authentication请求令牌
4.SecurityContextHolderAwareRequestFilter:包装请求对象request
5.JaasApiIntegrationFilter
6.RememberMeAuthenticationFilter:记住我服务处理
7.AnonymousAuthenticationFilter:匿名身份处理,更新SecurityContextHolder
8.ExceptionTranslationFilter:获任何Spring Security异常,以便可以返回HTTP错误响应或启动适当的AuthenticationEntryPoint
9.FilterSecurityInterceptor:用于保护web URI并在访问被拒绝时引发异常

6.2DelegatingFilterProxy

DelegatingFilterProxy是一个特殊的filter,存在于spring-web模块中。DelegatingFilterProxy通过继承GenericFilterBean使得自己变为了一个Filter(因为GenericFilterBean implements Filter)。它是一个Filter,其命名却以proxy结尾。

<filter>
 <filter-name>myFilter</filter-name>
 <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
 <filter-name>myFilter</filter-name>
 <url-pattern>/*</url-pattern>
</filter-mapping>

此配置是我们使用web.xml配置Filter时做法。但是与普通的Filter不同的是DelegatingFilterProxy并没有实际的过滤逻辑,他会尝试寻找filter-name节点所配置的myFilter,并将过滤行为委托给myFilter来处理。这种方式能够利用Spring丰富的依赖注入工具和生命周期接口,因此DelegatingFilterProxy提供了web.xml与应用程序上下文之间的链接。

6.3springSecurityFilterChain

spring security入口

在没有spring boot之前,我们要使用spring security的话,通常在web.xml中添加如下配置:

<filter>
 <filter-name>springSecurityFilterChain</filter-name>
 <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
 </filter>
 <filter-mapping>
 <filter-name>springSecurityFilterChain</filter-name>
 <url-pattern>/*</url-pattern>
 </filter-mapping>

这里配置的是DelegatingFilterProxy。它实际上会去找到filter-name节点中的Filter——springSecurityFilterChain,并将实际的过滤工作交给springSecurityFilterChain处理。

在使用spring boot之后,这一xml配置被Java类配置给代替了。**@EnableWebSecurity 注解,通过跟踪源码可以发现@EnableWebSecurity会加载WebSecurityConfiguration类,而WebSecurityConfiguration类中就有创建springSecurityFilterChain这个Filter**的代码:

6.4FilterChainProxy 和SecurityFilterChain小结

spring security 的核心是基于filter

入口filter是springSecurityFilterChain(它会被DelegatingFilterProxy委托来执行过滤任务)

springSecurityFilterChain实际上是FilterChainProxy (一个filter)

FilterChainProxy里边有一个SecurityFilterChain集合,doFIlter的时候会从其中取。

7.测试 通过数据库查询,储存用户和角色实现安全验证

准备:整合mybatis连接本地数据库

7.1创建User实体类

/**
 * ID
 */
private Integer id;

/**
 * 账号
 */
private String name;

/**
 * 密码
 */
private String password;

/**
 * 性别
 */
private String sex;

7.2自定义UserDetailsService

查询用户角色以及设置角色、即上面的userFromDatabase.getRole()通常是一个list、所以设置角色的时候,就是for循环new 多个SimpleGrantedAuthority并设置。

@Component("userDetailsService")
public class CustomUserDetailsService implements UserDetailsService {

    public static final PasswordEncoder PASSWORD_ENCODER = new BCryptPasswordEncoder();
    @Autowired
    private Userservice userservice;
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        // 1. 查询用户
        User user = userservice.getOne(username);
//        boolean s = PASSWORD_ENCODER.matches("123156", "$2a$10$69twjsLqHXDIxObXBTqV6uUrkp0UgglnTjPbQKSZyQibTq4VRKgsG");
        if (user==null){
            throw new UsernameNotFoundException("User " + username + " was not found in db");
            //这里找不到必须抛异常
        }
        // 2. 设置角色
        Collection<GrantedAuthority> grantedAuthorities = new ArrayList<>();
        GrantedAuthority grantedAuthority = new SimpleGrantedAuthority(user.getSex());
        grantedAuthorities.add(grantedAuthority);
        return new org.springframework.security.core.userdetails.User(username,user.getPassword(), grantedAuthorities);
    }
}

7.3修改之前的SecurityConfiguration,加入CustomUserDetailsServicebean配置如下

@Override
 protected void configure(AuthenticationManagerBuilder auth) throws Exception {
     auth.userDetailsService(userDetailsService)// 设置自定义的userDetailsService
     .passwordEncoder(passwordEncoder());
 }
 
 @Bean
 public PasswordEncoder passwordEncoder() {
     return new BCryptPasswordEncoder();
 }

8.测试 token式授权无session 状态

spring security 可以准确控制session何时创建以及Spring Security如何与之交互:

always 没有session就创建。

**ifRequired (default)**,如果需要就创建(默认)。

**never **

**stateless ** 不创建不使用session

需要在SecurityConfiguration中修改为stateless状态

http
 .sessionManagement()
 .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
标签: spring java 安全

本文转载自: https://blog.csdn.net/Jokers_lin/article/details/124726605
版权归原作者 夏天i(Joker) 所有, 如有侵权,请联系我们删除。

“Spring Security认证授权-权限验证使用教程(一)”的评论:

还没有评论