Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >spring security自定义指南

spring security自定义指南

作者头像
code4it
发布于 2018-09-17 07:33:32
发布于 2018-09-17 07:33:32
1.6K00
代码可运行
举报
文章被收录于专栏:码匠的流水账码匠的流水账
运行总次数:0
代码可运行

本文主要研究一下几种自定义spring security的方式

主要方式

  • 自定义UserDetailsService
  • 自定义passwordEncoder
  • 自定义filter
  • 自定义AuthenticationProvider
  • 自定义AccessDecisionManager
  • 自定义securityMetadataSource
  • 自定义access访问控制
  • 自定义authenticationEntryPoint
  • 自定义多个WebSecurityConfigurerAdapter

自定义UserDetailsService

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    //......
    @Bean
    @Override
    protected UserDetailsService userDetailsService(){
        InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
        manager.createUser(User.withUsername("demoUser1").password("123456")
                .authorities("ROLE_USER","read_x").build());
        manager.createUser(User.withUsername("admin").password("123456")
                .authorities("ROLE_ADMIN").build());
        return manager;
    }
}

通过重写userDetailsService()方法自定义userDetailsService。这里展示的是InMemoryUserDetailsManager。 spring security内置了JdbcUserDetailsManager,可以自行扩展

自定义passwordEncoder

自定义密码的加密方式,实例如下

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    //......

    @Bean
    public DaoAuthenticationProvider authenticationProvider() {
        final DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider();
        authProvider.setUserDetailsService(userDetailsService);
        authProvider.setPasswordEncoder(encoder());
        return authProvider;
    }

    @Bean
    public PasswordEncoder encoder() {
        return new BCryptPasswordEncoder(11);
    }
}

自定义filter

自定义filter离不开对spring security内置filter的顺序的认知:

Standard Filter Aliases and Ordering

spring security内置的各种filter顺序如下:

Alias

Filter Class

Namespace Element or Attribute

CHANNEL_FILTER

ChannelProcessingFilter

http/intercept-url@requires-channel

SECURITY_CONTEXT_FILTER

SecurityContextPersistenceFilter

http

CONCURRENT_SESSION_FILTER

ConcurrentSessionFilter

session-management/concurrency-control

HEADERS_FILTER

HeaderWriterFilter

http/headers

CSRF_FILTER

CsrfFilter

http/csrf

LOGOUT_FILTER

LogoutFilter

http/logout

X509_FILTER

X509AuthenticationFilter

http/x509

PRE_AUTH_FILTER

AbstractPreAuthenticatedProcessingFilter Subclasses

N/A

CAS_FILTER

CasAuthenticationFilter

N/A

FORM_LOGIN_FILTER

UsernamePasswordAuthenticationFilter

http/form-login

BASIC_AUTH_FILTER

BasicAuthenticationFilter

http/http-basic

SERVLET_API_SUPPORT_FILTER

SecurityContextHolderAwareRequestFilter

http/@servlet-api-provision

JAAS_API_SUPPORT_FILTER

JaasApiIntegrationFilter

http/@jaas-api-provision

REMEMBER_ME_FILTER

RememberMeAuthenticationFilter

http/remember-me

ANONYMOUS_FILTER

AnonymousAuthenticationFilter

http/anonymous

SESSION_MANAGEMENT_FILTER

SessionManagementFilter

session-management

EXCEPTION_TRANSLATION_FILTER

ExceptionTranslationFilter

http

FILTER_SECURITY_INTERCEPTOR

FilterSecurityInterceptor

http

SWITCH_USER_FILTER

SwitchUserFilter

N/A

内置的认证filter

  • UsernamePasswordAuthenticationFilter 参数有username,password的,走UsernamePasswordAuthenticationFilter,提取参数构造UsernamePasswordAuthenticationToken进行认证,成功则填充SecurityContextHolder的Authentication
  • BasicAuthenticationFilter header里头有Authorization,而且value是以Basic开头的,则走BasicAuthenticationFilter,提取参数构造UsernamePasswordAuthenticationToken进行认证,成功则填充SecurityContextHolder的Authentication
  • AnonymousAuthenticationFilter 给没有登陆的用户,填充AnonymousAuthenticationToken到SecurityContextHolder的Authentication

定义自己的filter

可以像UsernamePasswordAuthenticationFilter或者AnonymousAuthenticationFilter继承GenericFilterBean,或者像BasicAuthenticationFilter继承OncePerRequestFilter。 关于GenericFilterBean与OncePerRequestFilter的区别可以见这篇spring mvc中的几类拦截器对比

自定义filter主要完成功能如下:

  • 提取认证参数
  • 调用认证,成功则填充SecurityContextHolder的Authentication,失败则抛出异常

实例

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class DemoAuthFilter extends GenericFilterBean {

    private final AuthenticationManager authenticationManager;

    public DemoAuthFilter(AuthenticationManager authenticationManager) {
        this.authenticationManager = authenticationManager;
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest;
        HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse;

        String token = httpServletRequest.getHeader("app_token");
        if(StringUtils.isEmpty(token)){
            httpServletResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, "invalid token");
            return ;
        }

        try {
            Authentication auth = authenticationManager.authenticate(new WebToken(token));
            SecurityContextHolder.getContext().setAuthentication(auth);
            filterChain.doFilter(servletRequest, servletResponse);
        } catch (AuthenticationException e) {
            httpServletResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, e.getMessage());
        }
    }
}

设置filter顺序

上面定义完filter之后,然后就要将它放置到filterChain中

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    //......
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.addFilterBefore(new DemoAuthFilter(authenticationManager()), BasicAuthenticationFilter.class);
        http.csrf().disable();
        http.logout().disable();
        http.sessionManagement().disable();
    }
}

这里把他添加在BasicAuthenticationFilter之前,当然可以根据情况直接替换UsernamePasswordAuthenticationFilter

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
http.addFilterAt(new DemoAuthFilter(authenticationManager()),UsernamePasswordAuthenticationFilter.class);

自定义AuthenticationProvider

AuthenticationManager接口有个实现ProviderManager相当于一个provider chain,它里头有个List providers,通过provider来实现认证。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public Authentication authenticate(Authentication authentication)
            throws AuthenticationException {
        Class<? extends Authentication> toTest = authentication.getClass();
        AuthenticationException lastException = null;
        Authentication result = null;
        boolean debug = logger.isDebugEnabled();

        for (AuthenticationProvider provider : getProviders()) {
            if (!provider.supports(toTest)) {
                continue;
            }

            //......
            try {
                result = provider.authenticate(authentication);

                if (result != null) {
                    copyDetails(authentication, result);
                    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;
            }
        }

        //......
    }

AuthenticationProvider通过supports方法来标识它是否能够处理这个类型的Authentication。 AnonymousAuthenticationFilter构造的是AnonymousAuthenticationToken,由AnonymousAuthenticationProvider来处理

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class AnonymousAuthenticationProvider implements AuthenticationProvider,
        MessageSourceAware {
        //......
        public boolean supports(Class<?> authentication) {
            return (AnonymousAuthenticationToken.class.isAssignableFrom(authentication));
        }
}

UsernamePasswordAuthenticationFilter,BasicAuthenticationFilter构造的是UsernamePasswordAuthenticationToken,由DaoAuthenticationProvider(其父类为AbstractUserDetailsAuthenticationProvider)来处理

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public abstract class AbstractUserDetailsAuthenticationProvider implements
        AuthenticationProvider, InitializingBean, MessageSourceAware {
        //......
        public boolean supports(Class<?> authentication) {
            return (UsernamePasswordAuthenticationToken.class
                .isAssignableFrom(authentication));
        }
}

像上面我们自定义了WebToken,其实例如下:

可以实现Authentication接口,或者继承AbstractAuthenticationToken

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class WebToken extends AbstractAuthenticationToken {

    private final String token;

    public WebToken(String token) {
        super(null);
        this.token = token;
    }

    @Override
    public Object getCredentials() {
        return this.token;
    }

    @Override
    public Object getPrincipal() {
        return null;
    }
}

这里就自定义一下支持这类WebToken的AuthenticationProvider

AuthenticationProvider要实现的功能就是根据参数来校验是否可以登录通过,不通过则抛出异常;通过则获取其GrantedAuthority填充到authentication中 如果是继承了AbstractAuthenticationToken,则是填充其authorities属性 前面自定义的DemoAuthFilter会在登陆成功之后,将authentication写入到SecurityContextHolder的context中 可以实现AuthenticationProvider接口,或者继承AbstractUserDetailsAuthenticationProvider(默认集成了preAuthenticationChecks以及postAuthenticationChecks)

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Service
public class MyAuthProvider implements AuthenticationProvider {
    //...
    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        //......
    }
    @Override
    public boolean supports(Class<?> authenticationClass) {
        return return (WebToken.class
                .isAssignableFrom(authenticationClass));
    }
}

自定义AccessDecisionManager

前面有filter处理了登录问题,接下来是否可访问指定资源的问题就由FilterSecurityInterceptor来处理了。而FilterSecurityInterceptor是用了AccessDecisionManager来进行鉴权。

AccessDecisionManager的几个实现:

  • AffirmativeBased(spring security默认使用) 只要有投通过(ACCESS_GRANTED)票,则直接判为通过。如果没有投通过票且反对(ACCESS_DENIED)票在1个及其以上的,则直接判为不通过。
  • ConsensusBased(少数服从多数) 通过的票数大于反对的票数则判为通过;通过的票数小于反对的票数则判为不通过;通过的票数和反对的票数相等,则可根据配置allowIfEqualGrantedDeniedDecisions(默认为true)进行判断是否通过。
  • UnanimousBased(反对票优先) 无论多少投票者投了多少通过(ACCESS_GRANTED)票,只要有反对票(ACCESS_DENIED),那都判为不通过;如果没有反对票且有投票者投了通过票,那么就判为通过.

实例

其自定义方式之一可以参考聊聊spring security的role hierarchy,展示了如何自定义AccessDecisionVoter。

自定义securityMetadataSource

主要是通过ObjectPostProcessor来实现自定义,具体实例可参考spring security动态配置url权限

自定义access访问控制

对authorizeRequests的控制,可以使用permitAll,anonymous,authenticated,hasAuthority,hasRole等等

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
                .antMatchers("/login","/css/**", "/js/**","/fonts/**","/file/**").permitAll()
                .antMatchers("/anonymous*").anonymous()
                .antMatchers("/session").authenticated()
                .antMatchers("/login/impersonate").hasAuthority("ROLE_ADMIN")
                .antMatchers("/admin/**").hasRole("ADMIN")
                .antMatchers("/auth/*").hasAnyRole("ADMIN","USER")

这些都是利用spring security内置的表达式。像hasAuthority等,他们内部还是使用access方法来实现的。因此我们也可以直接使用access,来实现最大限度的自定义。

实例

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                .antMatchers("/login/**","/logout/**")
                .permitAll()
                .anyRequest().access("@authService.canAccess(request,authentication)");
    }
}

这个就有点像使用spring EL表达式,实现实例如下

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Component
public class AuthService {

    public boolean canAccess(HttpServletRequest request, Authentication authentication) {
        Object principal = authentication.getPrincipal();
        if(principal == null){
            return false;
        }

        if(authentication instanceof AnonymousAuthenticationToken){
            //check if this uri can be access by anonymous
            //return
        }

        Set<String> roles = authentication.getAuthorities()
                .stream()
                .map(e -> e.getAuthority())
                .collect(Collectors.toSet());
        String uri = request.getRequestURI();
        //check this uri can be access by this role

        return true;

    }
}

自定义authenticationEntryPoint

比如你想给basic认证换个realmName,除了再spring security配置中指定

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
security.basic.realm=myrealm

也可以这样

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
    httpBasic().authenticationEntryPoint(createBasicAuthEntryPoint("myrealm"))

    public static BasicAuthenticationEntryPoint createBasicAuthEntryPoint(String realmName){
        BasicAuthenticationEntryPoint entryPoint = new BasicAuthenticationEntryPoint();
        entryPoint.setRealmName(realmName);
        return entryPoint;
    }

自定义多个WebSecurityConfigurerAdapter

spring security使用antMatchers不支持not的情况,因此可以自定义多个WebSecurityConfigurerAdapter,利用order优先级来实现匹配的覆盖,具体可以参考这篇文章Multiple Entry Points in Spring Security

小结

还有其他自定义的方式,等后续有发现再补上。

doc

  • Spring Security password hashing example
  • spring mvc中的几类拦截器对比
  • spring security 自定义认证
  • Spring Security Tutorial
  • Security with Spring
  • 话说Spring Security权限管理(源码)
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2017-12-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 码匠的流水账 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Spring Security源码分析二:Spring Security授权过程
本文是接上一章Spring Security源码分析一:Spring Security认证过程进一步分析Spring Security用户名密码登录授权是如何实现得;
java干货
2021/02/19
9920
Spring Security源码分析二:Spring Security授权过程
Spring Security 工作原理概览
本文由读者 muggle 投稿,muggle 是一位具备极客精神的90后单身老实猿,对 Spring Security 有丰富的使用经验,muggle 个人博客地址是 https://muggle0.github.io。
江南一点雨
2019/05/13
1.5K1
Spring Security 工作原理概览
Spring Security 系列(1)
Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架。它提供了一组可以在Spring应用上下文中配置的Bean,充分利用了Spring IoC,DI(控制反转Inversion of Control ,DI:Dependency Injection 依赖注入)和AOP(面向切面编程)功能,为应用系统提供声明式的安全访问控制功能,减少了为企业系统安全控制编写大量重复代码的工作。
求和小熊猫
2022/06/12
1.1K0
Spring Security 系列(1)
权限框架 | 学会Spring Security权限框架,就是这么简单
Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架(简单说是对访问权限进行控制 )。
码神联盟
2019/07/31
4.5K0
权限框架 | 学会Spring Security权限框架,就是这么简单
Spring Boot:整合Spring Security
Spring Security 是 Spring 社区的一个顶级项目,也是 Spring Boot 官方推荐使用的安全框架。除了常规的认证(Authentication)和授权(Authorization)之外,Spring Security还提供了诸如ACLs,LDAP,JAAS,CAS等高级特性以满足复杂场景下的安全需求。另外,就目前而言,Spring Security和Shiro也是当前广大应用使用比较广泛的两个安全框架。
朝雨忆轻尘
2019/07/01
3.4K0
Spring Boot:整合Spring Security
Spring Security 案例实现和执行流程剖析
Spring Security 是 Spring 社区的一个顶级项目,也是 Spring Boot 官方推荐使用的安全框架。除了常规的认证(Authentication)和授权(Authorization)之外,Spring Security还提供了诸如ACLs,LDAP,JAAS,CAS等高级特性以满足复杂场景下的安全需求。
朝雨忆轻尘
2019/06/19
2.2K0
Spring Security 案例实现和执行流程剖析
Spring Security 架构与源码分析
Spring Security 主要实现了Authentication(认证,解决who are you? ) 和 Access Control(访问控制,也就是what are you allowe
JadePeng
2018/07/31
7710
Spring Security 架构与源码分析
Spring Security的认证和授权
Spring Security是为基于Spring的应用程序提供声明式安全保护的安全性框架。Spring Security提供了完整的安全性解决方案,它能够在Web请求级别和方法调用级别处理身份认证和授权。因为基于Spring框架,所以Spring Security充分利用了依赖注入(dependency injection,DI)和面向切面(AOP)的技术。
兜兜转转
2023/03/29
2.4K0
Spring Security的认证和授权
spring security 深度使用
springsecurity是一个典型的责任链模式;我们先新建一个springboot项目,进行最基本的springsecurity配置,然后debug;我这里使用的开发工具是idea.建议大家也使用idea来进行日常开发。好了话不多说,开始:
六个核弹
2022/12/23
5900
spring security 深度使用
一文理清SpringSecurity中基于用于名密码的登录认证流程
SpringSecurity作为一个出自Spring家族很强大的安全框架时长被引用到SpringBoot项目中用作登录认证和授权模块使用,但是对于大部分使用者来说都只停留在实现使用用户名和密码的方式登录。而对于企业的项目需求大多要实现多种登录认证方式,例如一个的登录功能往往需要支持下面几种登录模式:
用户3587585
2022/04/14
3.8K0
一文理清SpringSecurity中基于用于名密码的登录认证流程
Spring Boot + Spring Cloud 实现权限管理系统 后端篇(二十五):Spring Security 版本
到目前为止,我们使用的权限认证框架是 Shiro,虽然 Shiro 也足够好用并且简单,但对于 Spring 官方主推的安全框架 Spring Security,用户群也是甚大的,所以我们这里把当前的代码切分出一个 shiro-cloud 分支,作为 Shiro + Spring Cloud 技术的分支代码,dev 和 master 分支将替换为 Spring Security + Spring Cloud 的技术栈,并在后续计划中集成 Spring Security OAuth2 实现单点登录功能。
朝雨忆轻尘
2019/06/19
1.5K0
聊聊spring security的permitAll以及webIgnore
本文主要聊一下spring security的permitAll以及webIgnore的区别
code4it
2018/09/17
2K0
深入理解Spring Security授权机制原理
在Spring Security权限框架里,若要对后端http接口实现权限授权控制,有两种实现方式。
朱季谦
2020/12/28
2.7K0
深入理解Spring Security授权机制原理
Spring Security入门6:Spring Security的默认配置
Spring Security 是一个强大且灵活的身份验证和授权框架,用于保护 Java Web 应用程序中的资源,它提供了一套丰富的功能,用于处理身份验证、授权、密码编码和会话管理等安全相关的任务。
Designer 小郑
2024/01/11
1.1K0
Spring Security入门6:Spring Security的默认配置
Security使用笔记
UsernamePasswordAuthenticationFilter: 用来根据传递进来的用户名及密码进行用户认证。
doper
2022/09/26
5780
Security使用笔记
SpringBoot入门建站全系列(十二)Spring Security使用token做认证
Spring 是一个非常流行和成功的 Java 应用开发框架。Spring Security 基于 Spring 框架,提供了一套 Web 应用安全性的完整解决方案。一般来说,Web 应用的安全性包括用户认证(Authentication)和用户授权(Authorization)两个部分。用户认证指的是验证某个用户是否为系统中的合法主体,也就是说用户能否访问该系统。用户认证一般要求用户提供用户名和密码。系统通过校验用户名和密码来完成认证过程。用户授权指的是验证某个用户是否有权限执行某个操作。在一个系统中,不同用户所具有的权限是不同的。比如对一个文件来说,有的用户只能进行读取,而有的用户可以进行修改。一般来说,系统会为不同的用户分配不同的角色,而每个角色则对应一系列的权限。
品茗IT
2019/08/14
3.4K0
Springboot之Security前后端分离登录
Spring Security是一个功能强大且高度可定制的身份验证和访问控制框架。它是用于保护基于Spring的应用程序的实际标准。 Spring Security是一个框架,致力于为Java应用程序提供身份验证和授权。与所有Spring项目一样,Spring Security的真正强大之处在于可以轻松扩展以满足自定义要求 官方网站:https://spring.io/projects/spring-security#learn
海加尔金鹰
2020/09/16
9.5K3
在前后端分离项目中,如何使用Spring Security
在前后端分离的架构中,通常使用 Token 进行认证和授权是一种常见的做法。Token 可以是 JSON Web Token(JWT),用于在客户端和服务器之间传递身份信息和访问控制信息。下面我将详细介绍如何在 Spring Boot 后端和 Vue 前端应用中使用 Token(JWT)来实现认证和授权。
繁依Fanyi
2024/06/15
2420
浅析 Spring Security 的认证过程及相关过滤器
上一篇文章 浅析 Spring Security 核心组件 中介绍了Spring Security的基本组件,有了前面的基础,这篇文章就来详细分析下Spring Security的认证过程。
BUG弄潮儿
2022/12/05
6890
浅析 Spring Security 的认证过程及相关过滤器
Spring Security (四) 核心过滤器源码分析
前面的部分,我们关注了Spring Security是如何完成认证工作的,但是另外一部分核心的内容:过滤器,一直没有提到,我们已经知道Spring Security使用了springSecurityFillterChian作为了安全过滤的入口,这一节主要分析一下这个过滤器链都包含了哪些关键的过滤器,并且各自的使命是什么。 4 过滤器详解 4.1 核心过滤器概述 由于过滤器链路中的过滤较多,即使是Spring Security的官方文档中也并未对所有的过滤器进行介绍,在之前,《Spring Security(
程序猿DD
2018/02/01
1.5K0
Spring Security (四) 核心过滤器源码分析
推荐阅读
相关推荐
Spring Security源码分析二:Spring Security授权过程
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验