详解Spring Security认证流程

前言

Spring Seuciry相关的内容看了实在是太多了,但总觉得还是理解地不够巩固,还是需要靠知识输出做巩固。

相关版本:

java: jdk 8
spring-boot: 2.1.6.RELEASE 

过滤器链和认证过程

一个认证过程,其实就是过滤器链上的一个绿色矩形Filter所要执行的过程。

基本的认证过程有三步骤:

  1. Filter拦截请求,生成一个未认证的Authentication,交由AuthenticationManager进行认证;
  2. AuthenticationManager的默认实现ProviderManager会通过AuthenticationProviderAuthentication进行认证,其本身不做认证处理;
  3. 如果认证通过,则创建一个认证通过的Authentication返回;否则抛出异常,以表示认证不通过。

要理解这个过程,可以从类UsernamePasswordAuthenticationFilterProviderManagerDaoAuthenticationProviderInMemoryUserDetailsManagerUserDetailsService实现类,由UserDetailsServiceAutoConfiguration默认配置提供)进行了解。只要创建一个含有spring-boot-starter-security的springboot项目,在适当地打上断点接口看到这个流程。

用认证部门进行讲解

)

请求到前台之后,负责该请求的前台会将请求的内容封装为一个Authentication对象交给认证管理部门认证管理部门仅管理认证部门,不做具体的认证操作,具体的操作由与该前台相关的认证部门进行处理。当然,每个认证部门需要判断Authentication是否为该部门负责,是则由该部门负责处理,否则交给下一个部门处理。认证部门认证成功之后会创建一个认证通过的Authentication返回。否则要么抛出异常表示认证不通过,要么交给下一个部门处理。

如果需要新增认证类型,只要增加相应的前台(Filter)和与该前台(Filter)想对应的认证部门(AuthenticationProvider)就即可,当然也可以增加一个与已有前台对应的认证部门认证部门会通过前台生成的Authentication来判断该认证是否由该部门负责,因而也许提供一个两者相互认同的Authentication.

认证部门需要人员资料时,则可以从人员资料部门获取。不同的系统有不同的人员资料部门,需要我们提供该人员资料部门,否则将拿到空白档案。当然,人员资料部门不一定是唯一的,认证部门可以有自己的专属资料部门

上图还可以有如下的画法:

这个画法可能会和FilterChain更加符合。每一个前台其实就是FilterChain中的一个,客户拿着请求逐个前台请求认证,找到正确的前台之后进行认证判断。

前台(Filter)

这里的前台Filter仅仅指实现认证的Filter,Spring Security Filter Chain中处理这些Filter还有其他的Filter,比如CsrfFilter。如果非要给角色给他们,那么就当他们是保安人员吧。

Spring Security为我们提供了3个已经实现的Filter。UsernamePasswordAuthenticationFilterBasicAuthenticationFilterRememberMeAuthenticationFilter。如果不做任何个性化的配置,UsernamePasswordAuthenticationFilterBasicAuthenticationFilter会在默认的过滤器链中。这两种认证方式也就是默认的认证方式。

UsernamePasswordAuthenticationFilter仅仅会对/login路径生效,也就是说UsernamePasswordAuthenticationFilter负责发布认证,发布认证的接口为/login

public class UsernamePasswordAuthenticationFilter extends
    AbstractAuthenticationProcessingFilter {
  ...
  public UsernamePasswordAuthenticationFilter() {
    super(new AntPathRequestMatcher("/login", "POST"));
  }
  ...
}

UsernamePasswordAuthenticationFilter为抽象类AbstractAuthenticationProcessingFilter的一个实现,而BasicAuthenticationFilter为抽象类BasicAuthenticationFilter的一个实现。这四个类的源码提供了不错的前台(Filter)实现思路。

AbstractAuthenticationProcessingFilter

AbstractAuthenticationProcessingFilter 提供了认证前后需要做的事情,其子类只需要提供实现完成认证的抽象方法attemptAuthentication(HttpServletRequest, HttpServletResponse)即可。使用AbstractAuthenticationProcessingFilter时,需要提供一个拦截路径(使用AntPathMatcher进行匹配)来拦截对应的特定的路径。

UsernamePasswordAuthenticationFilter

UsernamePasswordAuthenticationFilter作为实际的前台,会将客户端提交的username和password封装成一个UsernamePasswordAuthenticationToken交给认证管理部门(AuthenticationManager)进行认证。如此,她的任务就完成了。

BasicAuthenticationFilter
前台(Filter)只会处理含有Authorization的Header,且小写化后的值以basic开头的请求,否则该前台(Filter)不负责处理。该Filter会从header中获取Base64编码之后的username和password,创建UsernamePasswordAuthenticationToken提供给认证管理部门(AuthenticationMananager)进行认证。

认证资料(Authentication)

前台接到请求之后,会从请求中获取所需的信息,创建自家认证部门(AuthenticationProvider)所认识的认证资料(Authentication)认证部门(AuthenticationProvider)则主要是通过认证资料(Authentication)的类型判断是否由该部门处理。

public interface Authentication extends Principal, Serializable {

  // 该principal具有的权限。AuthorityUtils工具类提供了一些方便的方法。
  Collection<? extends GrantedAuthority> getAuthorities();
  // 证明Principal的身份的证书,比如密码。
  Object getCredentials();
  // authentication request的附加信息,比如ip。
  Object getDetails();
  // 当事人。在username+password模式中为username,在有userDetails之后可以为userDetails。
  Object getPrincipal();
  // 是否已经通过认证。
  boolean isAuthenticated();
  // 设置通过认证。
  void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException;
}

Authentication被认证之后,会保存到一个thread-local的SecurityContext中。

// 设置
SecurityContextHolder.getContext().setAuthentication(anAuthentication);
// 获取
Authentication existingAuth = SecurityContextHolder.getContext()
        .getAuthentication();

在写前台Filter的时候,可以先检查SecurityContextHolder.getContext()中是否已经存在通过认证的Authentication了,如果存在,则可以直接跳过该Filter。已经通过验证的Authentication建议设置为一个不可修改的实例。

目前从Authentication的类图中看到的实现类,均为Authentication的抽象子类AbstractAuthenticationToken的实现类。实现类有好几个,与前面的讲到的Filter相关的有UsernamePasswordAuthenticationTokenRememberMeAuthenticationToken

AbstractAuthenticationTokenCredentialsContainerAuthentication的子类。实现了一些简单的方法,但主要的方法还需要实现。该类的getName()方法的实现可以看到常用的principal类为UserDetailsAuthenticationPrincipalPrincial。如果有需要将对象设置为principal,可以考虑继承这三个类中的一个。

public String getName() {
  if (this.getPrincipal() instanceof UserDetails) {
    return ((UserDetails) this.getPrincipal()).getUsername();
  }
  if (this.getPrincipal() instanceof AuthenticatedPrincipal) {
    return ((AuthenticatedPrincipal) this.getPrincipal()).getName();
  }
  if (this.getPrincipal() instanceof Principal) {
    return ((Principal) this.getPrincipal()).getName();
  }

  return (this.getPrincipal() == null) ? "" : this.getPrincipal().toString();
}

认证管理部门(AuthenticationManager)

AuthenticationManager是一个接口,认证Authentication,如果认证通过之后,返回的Authentication应该带上该principal所具有的GrantedAuthority

public interface AuthenticationManager {
  Authentication authenticate(Authentication authentication)
      throws AuthenticationException;
}

该接口的注释中说明,必须按照如下的异常顺序进行检查和抛出:

  • DisabledException:账号不可用
  • LockedException:账号被锁
  • BadCredentialsException:证书不正确

Spring Security提供一个默认的实现ProviderManager认证管理部门(ProviderManager)仅执行管理职能,具体的认证职能由认证部门(AuthenticationProvider)执行。

public class ProviderManager implements AuthenticationManager, MessageSourceAware,
    InitializingBean {
  ...

  public ProviderManager(List<AuthenticationProvider> providers) {
    this(providers, null);
  }

  public ProviderManager(List<AuthenticationProvider> providers,
      AuthenticationManager parent) {
    Assert.notNull(providers, "providers list cannot be null");
    this.providers = providers;
    this.parent = parent;
    checkState();
  }

  public Authentication authenticate(Authentication authentication)
      throws AuthenticationException {
    Class<? extends Authentication> toTest = authentication.getClass();
    AuthenticationException lastException = null;
    AuthenticationException parentException = null;
    Authentication result = null;
    Authentication parentResult = null;
    boolean debug = logger.isDebugEnabled();

    for (AuthenticationProvider provider : getProviders()) {
      // #1, 检查是否由该认证部门进行认证`AuthenticationProvider`
      if (!provider.supports(toTest)) {
        continue;
      }

      if (debug) {
        logger.debug("Authentication attempt using "
            + provider.getClass().getName());
      }

      try {
        // #2, 认证部门进行认证
        result = provider.authenticate(authentication);

        if (result != null) {
          copyDetails(authentication, result);
          // #3,认证通过则不再进行下一个认证部门的认证,否则抛出的异常被捕获,执行下一个认证部门(AuthenticationProvider)
          break;
        }
      }
      catch (AccountStatusException e) {
        prepareException(e, authentication);
        // SEC-546: Avoid polling additional providers if auth failure is due to
        // invalid account status
        throw e;
      }
      catch (InternalAuthenticationServiceException e) {
        prepareException(e, authentication);
        throw e;
      }
      catch (AuthenticationException e) {
        lastException = e;
      }
    }

    if (result == null && parent != null) {
      // Allow the parent to try.
      try {
        result = parentResult = parent.authenticate(authentication);
      }
      catch (ProviderNotFoundException e) {
        // ignore as we will throw below if no other exception occurred prior to
        // calling parent and the parent
        // may throw ProviderNotFound even though a provider in the child already
        // handled the request
      }
      catch (AuthenticationException e) {
        lastException = parentException = e;
      }
    }
    // #4, 如果认证通过,执行认证通过之后的操作
    if (result != null) {
      if (eraseCredentialsAfterAuthentication
          && (result instanceof CredentialsContainer)) {
        // Authentication is complete. Remove credentials and other secret data
        // from authentication
        ((CredentialsContainer) result).eraseCredentials();
      }

      // If the parent AuthenticationManager was attempted and successful than it will publish an AuthenticationSuccessEvent
      // This check prevents a duplicate AuthenticationSuccessEvent if the parent AuthenticationManager already published it
      if (parentResult == null) {
        eventPublisher.publishAuthenticationSuccess(result);
      }
      return result;
    }

    // Parent was null, or didn't authenticate (or throw an exception).
    // #5,如果认证不通过,必然有抛出异常,否则表示没有配置相应的认证部门(AuthenticationProvider)
    if (lastException == null) {
      lastException = new ProviderNotFoundException(messages.getMessage(
          "ProviderManager.providerNotFound",
          new Object[] { toTest.getName() },
          "No AuthenticationProvider found for {0}"));
    }

    // If the parent AuthenticationManager was attempted and failed than it will publish an AbstractAuthenticationFailureEvent
    // This check prevents a duplicate AbstractAuthenticationFailureEvent if the parent AuthenticationManager already published it
    if (parentException == null) {
      prepareException(lastException, authentication);
    }

    throw lastException;
  }
  ...
}

遍历所有的认证部门(AuthenticationProvider),找到支持的认证部门进行认证认证部门进行认证认证通过则不再进行下一个认证部门的认证,否则抛出的异常被捕获,执行下一个认证部门(AuthenticationProvider)如果认证通过,执行认证通过之后的操作如果认证不通过,必然有抛出异常,否则表示没有配置相应的认证部门(AuthenticationProvider)

当使用到Spring Security OAuth2的时候,会看到另一个实现OAuth2AuthenticationManager

认证部门(AuthenticationProvider)

认证部门(AuthenticationProvider)负责实际的认证工作,与认证管理部门(ProvderManager)协同工作。也许其他的认证管理部门(AuthenticationManager)并不需要认证部门(AuthenticationProvider)的协作。

public interface AuthenticationProvider {
  // 进行认证
  Authentication authenticate(Authentication authentication)
      throws AuthenticationException;
  // 是否由该AuthenticationProvider进行认证
  boolean supports(Class<?> authentication);
}

该接口有很多的实现类,其中包含了RememberMeAuthenticationProvider(直接AuthenticationProvider)和DaoAuthenticationProvider(通过AbastractUserDetailsAuthenticationProvider简介继承)。这里重点讲讲AbastractUserDetailsAuthenticationProviderDaoAuthenticationProvider

AbastractUserDetailsAuthenticationProvider

顾名思义,AbastractUserDetailsAuthenticationProvider是对UserDetails支持的Provider,其他的Provider,如RememberMeAuthenticationProvider就不需要用到UserDetails。该抽象类有两个抽象方法需要实现类完成:

// 获取 UserDetails
protected abstract UserDetails retrieveUser(String username,
    UsernamePasswordAuthenticationToken authentication)
    throws AuthenticationException;

protected abstract void additionalAuthenticationChecks(UserDetails userDetails,
    UsernamePasswordAuthenticationToken authentication)
    throws AuthenticationException;

retrieveUser()方法为校验提供UserDetails。先看下UserDetails:

public interface UserDetails extends Serializable {

  Collection<? extends GrantedAuthority> getAuthorities();

  String getPassword();

  String getUsername();
  // 账号是否过期
  boolean isAccountNonExpired();
  // 账号是否被锁
  boolean isAccountNonLocked();
  // 证书(password)是否过期
  boolean isCredentialsNonExpired();
  // 账号是否可用
  boolean isEnabled();
}

AbastractUserDetailsAuthenticationProvider#authentication(Authentication)分为三步验证:

  • preAuthenticationChecks.check(user);
  • additionalAuthenticationChecks(user,
  • (UsernamePasswordAuthenticationToken) authentication);
  • postAuthenticationChecks.check(user);

preAuthenticationChecks的默认实现为DefaultPreAuthenticationChecks,负责完成校验:

  1. UserDetails#isAccountNonLocked()
  2. UserDetails#isEnabled()
  3. UserDetails#isAccountNonExpired()

postAuthenticationChecks的默认实现为DefaultPostAuthenticationChecks,负责完成校验:

UserDetails#user.isCredentialsNonExpired()

additionalAuthenticationChecks需要由实现类完成。

校验成功之后,AbstractUserDetailsAuthenticationProvider会创建并返回一个通过认证的Authentication

protected Authentication createSuccessAuthentication(Object principal,
    Authentication authentication, UserDetails user) {
  // Ensure we return the original credentials the user supplied,
  // so subsequent attempts are successful even with encoded passwords.
  // Also ensure we return the original getDetails(), so that future
  // authentication events after cache expiry contain the details
  UsernamePasswordAuthenticationToken result = new UsernamePasswordAuthenticationToken(
      principal, authentication.getCredentials(),
      authoritiesMapper.mapAuthorities(user.getAuthorities()));
  result.setDetails(authentication.getDetails());

  return result;
}

DaoAuthenticationProvider

如下为DaoAuthenticationProviderAbstractUserDetailsAuthenticationProvider抽象方法的实现。

// 检查密码是否正确
protected void additionalAuthenticationChecks(UserDetails userDetails,
    UsernamePasswordAuthenticationToken authentication)
    throws AuthenticationException {
  if (authentication.getCredentials() == null) {
    logger.debug("Authentication failed: no credentials provided");

    throw new BadCredentialsException(messages.getMessage(
        "AbstractUserDetailsAuthenticationProvider.badCredentials",
        "Bad credentials"));
  }

  String presentedPassword = authentication.getCredentials().toString();

  if (!passwordEncoder.matches(presentedPassword, userDetails.getPassword())) {
    logger.debug("Authentication failed: password does not match stored value");

    throw new BadCredentialsException(messages.getMessage(
        "AbstractUserDetailsAuthenticationProvider.badCredentials",
        "Bad credentials"));
  }
}
// 通过资料室(UserDetailsService)获取UserDetails对象
protected final UserDetails retrieveUser(String username,
    UsernamePasswordAuthenticationToken authentication)
    throws AuthenticationException {
  prepareTimingAttackProtection();
  try {
    UserDetails loadedUser = this.getUserDetailsService().loadUserByUsername(username);
    if (loadedUser == null) {
      throw new InternalAuthenticationServiceException(
          "UserDetailsService returned null, which is an interface contract violation");
    }
    return loadedUser;
  }
  ...
}

在以上的代码中,需要提供UserDetailsServicePasswordEncoder实例。只要实例化这两个类,并放入到Spring容器中即可。

资料部门(UserDetailsService)

UserDetailsService接口提供认证过程所需的UserDetails的类,如DaoAuthenticationProvider需要一个UserDetailsService实例。

public interface UserDetailsService {
  UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
}

Spring Security提供了两个UserDetailsService的实现:InMemoryUserDetailsManagerJdbcUserDetailsManagerInMemoryUserDetailsManager为默认配置,从UserDetailsServiceAutoConfiguration的配置中可以看出。当然也不容易理解,基于数据库的实现需要增加数据库的配置,不适合做默认实现。这两个类均为UserDetailsManager的实现类,UserDetailsManager定义了UserDetails的CRUD操作。InMemoryUserDetailsManager使用Map<String, MutableUserDetails>做存储。

public interface UserDetailsManager extends UserDetailsService {
  void createUser(UserDetails user);

  void updateUser(UserDetails user);

  void deleteUser(String username);

  void changePassword(String oldPassword, String newPassword);

  boolean userExists(String username);
}

如果我们需要增加一个UserDetailsService,可以考虑实现UserDetailsService或者UserDetailsManager

增加一个认证流程

到这里,我们已经知道Spring Security的流程了。从上面的内容可以知道,如要增加一个新的认证方式,只要增加一个[前台(Filter) + 认证部门(AuthenticationProvider) + 资料室(UserDetailsService)]组合即可。事实上,资料室(UserDetailsService)不是必须的,可根据认证部门(AuthenticationProvider)需要实现。

我会在另一篇文章中以手机号码+验证码登录为例进行讲解。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • SpringSecurity的防Csrf攻击实现代码解析

    CSRF(Cross-site request forgery)跨站请求伪造,也被称为One Click Attack或者Session Riding,通常缩写为CSRF或XSRF,是一种对网站的恶意利用.尽管听起来像跨站脚本(XSS),但它与XSS非常不同,XSS利用站点内的信任用户,而CSRF则通过伪装成受信任用户的请求来利用受信任的网站.与XSS攻击相比,CSRF攻击往往不大流行(因此对其进行防范的资源也相当稀少)和难以防范,所以被认为比XSS更具危险性. CSRF是一种依赖web浏览器的

  • SpringSecurity rememberme功能实现过程解析

    记住我功能原理分析 还记得前面咱们分析认证流程时,提到的记住我功能吗? 现在继续跟踪找到AbstractRememberMeServices对象的loginSuccess方法: 再点进去上面if判断中的rememberMeRequested方法,还在当前类中: 如果上面方法返回true,就表示页面勾选了记住我选项了. 继续顺着调用的方法找到PersistentTokenBasedRememberMeServices的onLoginSuccess方法: 注意name和value属性的值不要写错哦!

  • Spring Security使用数据库认证及用户密码加密和解密功能

    流程图: 1.接上一篇博客https://mp.csdn.net/console/editor/html/104576494,准备好环境. 2.spring-security.xml中的配置: <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:security="

  • SpringSecurity权限控制实现原理解析

    菜单控制: 可以用来判断这个用户是不是有这些角色,没有的话就不展示 数据控制: 由于数据都是从后端查的,在后端控制权限就可以了 <!-- 开启权限控制注解支持 jsr250-annotations="enabled"表示支持jsr250-api的注解,需要jsr250-api的jar包 pre-post-annotations="enabled"表示支持spring表达式注解 secured-annotations="enabled"这才是

  • Spring security BCryptPasswordEncoder密码验证原理详解

    一.加密算法和hash算法的区别 加密算法是一种可逆的算法,基本过程就是对原来为明文的文件或数据按某种算法进行处理,使其成为不可读的一段代码为"密文",但在用相应的密钥进行操作之后就可以得到原来的内容 . 哈希算法是一种不可逆的算法,是把任意长度的输入通过散列算法变换成固定长度的输出,输出就是散列值,不同的输入可能会散列成相同的输出,所以不可能从散列值来确定唯一的输入值. 二.源码解析 BCryptPasswordEncoder类实现了PasswordEncoder接口,这个接口中定义

  • Spring security实现对账户进行加密

    一.原理分析1.1加密原理 首先前端页面发送注册的账户信息到controller层,然后依次经过service层和dao层,最后入库.其中对密码的加密应该放在service层进行,加密后再入库. spring security中有一个加密类BCryptPasswordEncoder可以用来对密码进行加密,调用其中的encode方法返回一个加密后的字符串 public String encode(CharSequence rawPassword) { String salt; if (streng

  • Spring Security和Shiro的相同点与不同点整理

    Spring Security和Shiro的区别 相同点 1.认证功能 2.授权功能 3.加密功能 4.会话管理 5.缓存支持 6.rememberMe功能 ... 不同点 1.Spring Security 基于Spring 开发,项目若使用 Spring 作为基础,配合 Spring Security 做权限更加方便,而 Shiro 需要和 Spring 进行整合开发: 2.Spring Security 功能比 Shiro 更加丰富些,例如安全维护方面: 3.Spring Security

  • Spring Security常用过滤器实例解析

    Spring Security常见的15个拦截器 1 . org.springframework.security.web.context.SecurityContextPersistenceFilter 首当其冲的一个过滤器,作用之重要,自不必多言. SecurityContextPersistenceFilter主要是使用SecurityContextRepository在session中保存或更新一个 SecurityContext,并将SecurityContext给以后的过滤器使用,来

  • 详解Spring Security认证流程

    前言 Spring Seuciry相关的内容看了实在是太多了,但总觉得还是理解地不够巩固,还是需要靠知识输出做巩固. 相关版本: java: jdk 8 spring-boot: 2.1.6.RELEASE 过滤器链和认证过程 一个认证过程,其实就是过滤器链上的一个绿色矩形Filter所要执行的过程. 基本的认证过程有三步骤: Filter拦截请求,生成一个未认证的Authentication,交由AuthenticationManager进行认证: AuthenticationManager的

  • 详解spring security四种实现方式

    spring security实现方式大致可以分为这几种: 1.配置文件实现,只需要在配置文件中指定拦截的url所需要权限.配置userDetailsService指定用户名.密码.对应权限,就可以实现. 2.实现UserDetailsService,loadUserByUsername(String userName)方法,根据userName来实现自己的业务逻辑返回UserDetails的实现类,需要自定义User类实现UserDetails,比较重要的方法是getAuthorities()

  • 详解Spring Security 中的四种权限控制方式

    Spring Security 中对于权限控制默认已经提供了很多了,但是,一个优秀的框架必须具备良好的扩展性,恰好,Spring Security 的扩展性就非常棒,我们既可以使用 Spring Security 提供的方式做授权,也可以自定义授权逻辑.一句话,你想怎么玩都可以! 今天松哥来和大家介绍一下 Spring Security 中四种常见的权限控制方式. 表达式控制 URL 路径权限 表达式控制方法权限 使用过滤注解 动态权限 四种方式,我们分别来看.  1.表达式控制 URL 路径权

  • 一文详解Spring Security的基本用法

    目录 1.引入依赖 2.用户名和密码在哪里设置 3.UserDetailsService接口详解 3.1JdbcDaoImpl实现类 3.2InMemoryUserDetailsManager实现类 3.3自定义实现类实现UserDetailsService接口 4.如何修改登录页面 Spring Security是一个功能强大且高度可定制的身份验证和访问控制框架, 提供了完善的认证机制和方法级的授权功能.是一款非常优秀的权限管理框架.它的核心是一组过滤器链,不同的功能经由不同的过滤器. 今天通

  • 详解Spring 拦截器流程及多个拦截器的执行顺序

    拦截器是 Spring MVC 中的组件,它可以在进入请求方法前做一些操作,也可以在请求方法后和渲染视图后做一些事情. 拦截器的定义 SpringMVC 的拦截器只需要实现 HandlerInterceptor 接口,并进行配置即可.HandlerInterceptor 接口的定义如下: public interface HandlerInterceptor { default boolean preHandle(HttpServletRequest request, HttpServletRe

  • 详解Spring Security中获取当前登录用户的详细信息的几种方法

    目录 在Bean中获取用户信息 在Controller中获取用户信息 通过 Interface 获取用户信息 在JSP页面中获取用户信息 在Bean中获取用户信息 Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); if (!(authentication instanceof AnonymousAuthenticationToken)) { String currentU

  • 详解Spring Security中权限注解的使用

    目录 1. 具体用法 2. SpEL 3. @PreAuthorize 最近有个小伙伴在微信群里问 Spring Security 权限注解的问题: 很多时候事情就是这么巧,松哥最近在做的 tienchin 也是基于注解来处理权限问题的,所以既然大家有这个问题,咱们就一块来聊聊这个话题. 当然一些基础的知识我就不讲了,对于 Spring Security 基本用法尚不熟悉的小伙伴,可在公众号后台回复 ss,有原创的系列教程. 1. 具体用法 先来看看 Spring Security 权限注解的具

  • 详解Spring Security如何在权限中使用通配符

    目录 前言 1. SpEL 2. 自定义权限该如何写 3. 权限通配符 4. TienChin 项目怎么做的 前言 小伙伴们知道,在 Shiro 中,默认是支持权限通配符的,例如系统用户有如下一些权限: system:user:add system:user:delete system:user:select system:user:update … 现在给用户授权的时候,我们可以像上面这样,一个权限一个权限的配置,也可以直接用通配符: system:user:* 这个通配符就表示拥有针对用户的

  • 详解Spring Security的formLogin登录认证模式

    一.formLogin的应用场景 在本专栏之前的文章中,已经给大家介绍过Spring Security的HttpBasic模式,该模式比较简单,只是进行了通过携带Http的Header进行简单的登录验证,而且没有定制的登录页面,所以使用场景比较窄. 对于一个完整的应用系统,与登录验证相关的页面都是高度定制化的,非常美观而且提供多种登录方式.这就需要Spring Security支持我们自己定制登录页面,也就是本文给大家介绍的formLogin模式登录认证模式. 准备工作 新建一个Spring B

  • 详解Spring Security如何配置JSON登录

    spring security用了也有一段时间了,弄过异步和多数据源登录,也看过一点源码,最近弄rest,然后顺便搭oauth2,前端用json来登录,没想到spring security默认居然不能获取request中的json数据,谷歌一波后只在stackoverflow找到一个回答比较靠谱,还是得要重写filter,于是在这里填一波坑. 准备工作 基本的spring security配置就不说了,网上一堆例子,只要弄到普通的表单登录和自定义UserDetailsService就可以.因为需

随机推荐