Spring Security实现HTTP认证

目录
  • 前言
  • 一、HTTP基本认证是什么?
  • 二、HTTP基本认证流程
  • 一.Spring Security使用HTTP基本认证
    • 1.创建项目spring-security-http-auth
    • 2.创建配置文件WebSecurityConfig
    • 3.运行项目
  • 二.Spring Security HTTP基本认证原理
  • 三.HTTP摘要认证是什么?
  • 四.Spring Security使用HTTP摘要认证流程?
    • 运行项目

Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架。它提供了一组可以在Spring应用上下文中配置的Bean,充分利用了Spring IoC(控制反转),DI(依赖注入)和AOP(面向切面编程)功能,为应用系统提供声明式的安全访问控制功能,减少了为企业系统安全控制编写大量重复代码的工作。

前言

除系统内维护的用户名和密码认证技术外,Spring Security还支持HTTP层面的认证,包括HTTP基本认证和HTTP摘要认证

一、HTTP基本认证是什么?

HTTP基本认证是在RFC2616中定义的一种认证模式。

二、HTTP基本认证流程

  • 客户端发起一条没有携带认证信息的请求。
  • 服务器返回一条401 Unauthorized响应, 并在WWW-Authentication首部说明认证形式, 当进行HTTP基本认证时, WWW-Authentication会被设置为Basic realm=“被保护页面”。
  • 客户端收到401 Unauthorized 响应后, 弹出对话框, 询问用户名和密码。 当用户完成后, 客户端将用户名和密码使用冒号拼接并编码为Base64形式, 然后放入请求的Authorization首部发送给服务器。
  • 服务器解码得到客户端发来的用户名和密码,并在验证它们是正确的之后,返回客户端请求的报文

有上面可以看出只需要验证Authentication即可,因此如果不使用浏览器访问HTTP基本认证保护的页面,则自行在请求头中设置Authorization也是可以.

HTTP基本认证是一种无状态的认证方式,与表单认证相比,HTTP基本认证是一种基于HTTP层面的认证方式,无法携带session,即无法实现Remember-ME功能。另外,用户名和密码在传递时仅做一次简单的Base64编码,几乎等同于明文传输,极易出现密码被窃听和重放攻击等安全性问题,在实际系统开发中很少使用这种方式来进行安全验证。 如果有必要,也应使用加密的传输层HTTPS来保障安全.

一.Spring Security使用HTTP基本认证

1.创建项目spring-security-http-auth

pom.xml:

<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>

2.创建配置文件WebSecurityConfig

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests().anyRequest().authenticated().and().httpBasic();
    }
}

上面的配置最后添加了httpBasic(),使用http基本认证

3.运行项目

访问本地项目,http://localhost:8080

会弹出登陆框,我们看到调试工具中返回了401无权限。

我们使用Spring Security提供的默认的用户名和密码登陆。

登陆成功后,header中就会有Authorization: Basic dXNlcjo0NWU2NzViOC1hZGYwLTQzNzMtYjA2MS02MGE0YzkzZjA2ZGU=

二.Spring Security HTTP基本认证原理

上面我们实现了HTTP基本认证,我们看看其中Spring Security中是如何做到的?
我们使用HTTP基本认证的时候,在配置类中使用httpBasic()进行处理。
httpBasic方法:

public HttpBasicConfigurer<HttpSecurity> httpBasic() throws Exception {
        return (HttpBasicConfigurer)this.getOrApply(new HttpBasicConfigurer());
    }

上面可以看出,Spring Security进行HTTP基本认证是使用HttpBasicConfigurer配置类进行的。
HttpBasicConfigurer.class:

//构建HttpBasicConfigurer
public HttpBasicConfigurer() {
        this.realmName("Realm");
        LinkedHashMap<RequestMatcher, AuthenticationEntryPoint> entryPoints = new LinkedHashMap();
        entryPoints.put(X_REQUESTED_WITH, new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED));
        DelegatingAuthenticationEntryPoint defaultEntryPoint = new DelegatingAuthenticationEntryPoint(entryPoints);
        defaultEntryPoint.setDefaultEntryPoint(this.basicAuthEntryPoint);
        this.authenticationEntryPoint = defaultEntryPoint;
    }
//进行配置
public void configure(B http) {
		//进行认证管理
        AuthenticationManager authenticationManager = (AuthenticationManager)http.getSharedObject(AuthenticationManager.class);
        //声明basic认证拦截器
        BasicAuthenticationFilter basicAuthenticationFilter = new BasicAuthenticationFilter(authenticationManager, this.authenticationEntryPoint);
        if (this.authenticationDetailsSource != null) {
            basicAuthenticationFilter.setAuthenticationDetailsSource(this.authenticationDetailsSource);
        }
		//注册一个RememberMeServices
        RememberMeServices rememberMeServices = (RememberMeServices)http.getSharedObject(RememberMeServices.class);
        if (rememberMeServices != null) {
			//设置rememberMeServices
            basicAuthenticationFilter.setRememberMeServices(rememberMeServices);
        }
		//申明basicAuthenticationFilter过滤器
        basicAuthenticationFilter = (BasicAuthenticationFilter)this.postProcess(basicAuthenticationFilter);
        http.addFilter(basicAuthenticationFilter);
    }

上面声明BasicAuthenticationFilter并添加到拦截器链中
BasicAuthenticationFilter.class:

    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
        try {
        	//获取token
            UsernamePasswordAuthenticationToken authRequest = this.authenticationConverter.convert(request);
            //authRequest为空直接放行
            if (authRequest == null) {
                this.logger.trace("Did not process authentication request since failed to find username and password in Basic Authorization header");
                chain.doFilter(request, response);
                return;
            }
			//获取用户名
            String username = authRequest.getName();
            this.logger.trace(LogMessage.format("Found username '%s' in Basic Authorization header", username));
            if (this.authenticationIsRequired(username)) {
                Authentication authResult = this.authenticationManager.authenticate(authRequest);
                //创建上下文
                SecurityContext context = SecurityContextHolder.createEmptyContext();
                context.setAuthentication(authResult);
                //设置响应的上下文
                SecurityContextHolder.setContext(context);
                if (this.logger.isDebugEnabled()) {
                    this.logger.debug(LogMessage.format("Set SecurityContextHolder to %s", authResult));
                }

                this.rememberMeServices.loginSuccess(request, response, authResult);
                this.onSuccessfulAuthentication(request, response, authResult);
            }
        } catch (AuthenticationException var8) {
            SecurityContextHolder.clearContext();
            this.logger.debug("Failed to process authentication request", var8);
            this.rememberMeServices.loginFail(request, response);
            this.onUnsuccessfulAuthentication(request, response, var8);
            if (this.ignoreFailure) {
                chain.doFilter(request, response);
            } else {
                this.authenticationEntryPoint.commence(request, response, var8);
            }

            return;
        }

        chain.doFilter(request, response);
    }

BasicAuthenticationEntryPoint返回进行响应的处理

 public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException {
 		//添加响应响应头
        response.addHeader("WWW-Authenticate", "Basic realm=\"" + this.realmName + "\"");
        response.sendError(HttpStatus.UNAUTHORIZED.value(), HttpStatus.UNAUTHORIZED.getReasonPhrase());
    }

三.HTTP摘要认证是什么?

HTTP摘要认证和HTTP基本认证一样,也是在RFC2616中定义的认证模式,RFC2617专门对这两种认证模式做了规定。与 HTTP 基本认证相比,HTTP 摘要认证使用对通信双方都可知的口令进行校验,且最终的传输数据并非明文形式。

摘要认证是一种协议规定的Web服务器用来同网页浏览器进行认证信息协商的方法。它在密码发出前,先对其应用哈希函数,这相对于HTTP基本认证发送明文而言,更安全。

从技术上讲,摘要认证是使用随机数来阻止进行密码分析的MD5加密哈希函数应用。
HTTP摘要认证流程:

HTTP摘要认证中的相关参数:

  • username: 用户名。
  • password: 用户密码。
  • realm: 认证域, 由服务器返回。
  • opaque: 透传字符串, 客户端应原样返回。
  • method: 请求的方法。
  • nonce: 由服务器生成的随机字符串。
  • nc: 即nonce-count, 指请求的次数, 用于计数, 防止重放攻击。 qop被指定时, nc也必须被指定。
  • cnonce: 客户端发给服务器的随机字符串, qop被指定时, cnonce也必须被指定。
  • qop: 保护级别, 客户端根据此参数指定摘要算法。 若取值为auth, 则只进行身份验证; 若取
  • 值为auth-int, 则还需要校验内容完整性。
  • uri: 请求的uri。
  • response:客户端根据算法算出的摘要值。
  • algorithm:摘要算法, 目前仅支持MD5。
  • entity-body:页面实体,非消息实体,仅在auth-int中支持。
  • 通常服务器携带的数据包括realm、 opaque、 nonce、 qop等字段, 如果客户端需要做出验证回应,就必须按照一定的算法计算得到一些新的数据并一起返回。

四.Spring Security使用HTTP摘要认证流程?

在Spring Security中没有像HTTP基础认证那样,通过httpBasic()方法进行集成HTTP摘要认证,但是Spring Security提供了像BasicAuthenticationEntryPoint一样的DigestAuthenticationEntryPoint.就是我们需要将DigestAuthenticationEntryPoint添加到filter过滤器中去处理。
代码如下:
WebSecurityConfig类:

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private DigestAuthenticationEntryPoint digestAuthenticationEntryPoint;

    @Autowired
    private UserDetailsService userDetailsService;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests().anyRequest().authenticated().and()
                .exceptionHandling()
                .authenticationEntryPoint(digestAuthenticationEntryPoint)
                .and().addFilter(digestAuthenticationFilter());
    }

    public DigestAuthenticationFilter digestAuthenticationFilter(){
        DigestAuthenticationFilter digestAuthenticationFilter = new DigestAuthenticationFilter();
        digestAuthenticationFilter.setUserDetailsService(userDetailsService);
        digestAuthenticationFilter.setAuthenticationEntryPoint(digestAuthenticationEntryPoint);
        return digestAuthenticationFilter;
    }

}

申明DigestAuthenticationEntryPointBean:

@Bean
    public DigestAuthenticationEntryPoint digestAuthenticationEntryPoint(){
        DigestAuthenticationEntryPoint digestAuthenticationEntryPoint = new DigestAuthenticationEntryPoint();
        digestAuthenticationEntryPoint.setRealmName("realName");
        digestAuthenticationEntryPoint.setKey("tony");
        return digestAuthenticationEntryPoint;
    }
@Bean
    public DigestAuthenticationEntryPoint digestAuthenticationEntryPoint(){
        DigestAuthenticationEntryPoint digestAuthenticationEntryPoint = new DigestAuthenticationEntryPoint();
        digestAuthenticationEntryPoint.setRealmName("realm");
        digestAuthenticationEntryPoint.setKey("tony");
        return digestAuthenticationEntryPoint;
    }

    @Bean
    public UserDetailsService userDetailsService() {
        InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
        manager.createUser(User.withUsername("tony").password("123456").roles("admin").build());
        return manager;
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return NoOpPasswordEncoder.getInstance();
    }

运行项目

访问主页,http://localhost:8080,返回如下页面:

我们输入用户名和密码登陆。

当长时间未登录,随机字符串到期了也登陆不上。
默认的过期时间为300s,我们可以通过设置时间。
DigestAuthenticationEntryPoint中realmName和key是必须要设置的。
相关源码:

public void afterPropertiesSet() {
        Assert.hasLength(this.realmName, "realmName must be specified");
        Assert.hasLength(this.key, "key must be specified");
    }

 public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException {
 		//计算过期时间
        long expiryTime = System.currentTimeMillis() + (long)(this.nonceValiditySeconds * 1000);
        //计算签名值
        String signatureValue = DigestAuthUtils.md5Hex(expiryTime + ":" + this.key);
        //随机字符串
        String nonceValue = expiryTime + ":" + signatureValue;
        //随机字符串base64
        String nonceValueBase64 = new String(Base64.getEncoder().encode(nonceValue.getBytes()));
        String authenticateHeader = "Digest realm=\"" + this.realmName + "\", qop=\"auth\", nonce=\"" + nonceValueBase64 + "\"";
        if (authException instanceof NonceExpiredException) {
            authenticateHeader = authenticateHeader + ", stale=\"true\"";
        }

        logger.debug(LogMessage.format("WWW-Authenticate header sent to user agent: %s", authenticateHeader));
        response.addHeader("WWW-Authenticate", authenticateHeader);
        response.sendError(HttpStatus.UNAUTHORIZED.value(), HttpStatus.UNAUTHORIZED.getReasonPhrase());
    }

进行处理的时候使用DigestAuthenticationFilter进行处理

public void afterPropertiesSet() {
		//必须设置userDetailsService
        Assert.notNull(this.userDetailsService, "A UserDetailsService is required");
        //必须设置authenticationEntryPoint
        Assert.notNull(this.authenticationEntryPoint, "A DigestAuthenticationEntryPoint is required");
    }

private void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
        String header = request.getHeader("Authorization");
        if (header != null && header.startsWith("Digest ")) {
            logger.debug(LogMessage.format("Digest Authorization header received from user agent: %s", header));
            DigestAuthenticationFilter.DigestData digestAuth = new DigestAuthenticationFilter.DigestData(header);

            try {
				//验证并且解密
                digestAuth.validateAndDecode(this.authenticationEntryPoint.getKey(), this.authenticationEntryPoint.getRealmName());
            } catch (BadCredentialsException var11) {
                this.fail(request, response, var11);
                return;
            }
            //缓存
            boolean cacheWasUsed = true;
            //缓存用户数据
            UserDetails user = this.userCache.getUserFromCache(digestAuth.getUsername());

            String serverDigestMd5;
            try {
                if (user == null) {
                    cacheWasUsed = false;
                    user = this.userDetailsService.loadUserByUsername(digestAuth.getUsername());
                    if (user == null) {
                        throw new AuthenticationServiceException("AuthenticationDao returned null, which is an interface contract violation");
                    }

                    this.userCache.putUserInCache(user);
                }
				//服务器md5摘要
                serverDigestMd5 = digestAuth.calculateServerDigest(user.getPassword(), request.getMethod());
                if (!serverDigestMd5.equals(digestAuth.getResponse()) && cacheWasUsed) {
                    logger.debug("Digest comparison failure; trying to refresh user from DAO in case password had changed");
                    user = this.userDetailsService.loadUserByUsername(digestAuth.getUsername());
                    this.userCache.putUserInCache(user);
                    serverDigestMd5 = digestAuth.calculateServerDigest(user.getPassword(), request.getMethod());
                }
            } catch (UsernameNotFoundException var12) {
                String message = this.messages.getMessage("DigestAuthenticationFilter.usernameNotFound", new Object[]{digestAuth.getUsername()}, "Username {0} not found");
                this.fail(request, response, new BadCredentialsException(message));
                return;
            }

            String message;
            if (!serverDigestMd5.equals(digestAuth.getResponse())) {
                logger.debug(LogMessage.format("Expected response: '%s' but received: '%s'; is AuthenticationDao returning clear text passwords?", serverDigestMd5, digestAuth.getResponse()));
                message = this.messages.getMessage("DigestAuthenticationFilter.incorrectResponse", "Incorrect response");
                this.fail(request, response, new BadCredentialsException(message));
            } else if (digestAuth.isNonceExpired()) {
                message = this.messages.getMessage("DigestAuthenticationFilter.nonceExpired", "Nonce has expired/timed out");
                this.fail(request, response, new NonceExpiredException(message));
            } else {
                logger.debug(LogMessage.format("Authentication success for user: '%s' with response: '%s'", digestAuth.getUsername(), digestAuth.getResponse()));
                Authentication authentication = this.createSuccessfulAuthentication(request, user);
                SecurityContext context = SecurityContextHolder.createEmptyContext();
                context.setAuthentication(authentication);
                SecurityContextHolder.setContext(context);
                chain.doFilter(request, response);
            }
        } else {
            chain.doFilter(request, response);
        }
    }

DigestData为摘要数据:

 private class DigestData {
//用户名
    private final String username;
    //认证域
    private final String realm;
    //随机字符串
    private final String nonce;
    private final String uri;
    private final String response;
    //保护级别
    private final String qop;
    //即nonce-count, 指请求的次数, 用于计数, 防止重放攻击
    private final String nc;
    private final String cnonce;
    private final String section212response;
    private long nonceExpiryTime;

    DigestData(String header) {
        this.section212response = header.substring(7);
        String[] headerEntries = DigestAuthUtils.splitIgnoringQuotes(this.section212response, ',');
        Map<String, String> headerMap = DigestAuthUtils.splitEachArrayElementAndCreateMap(headerEntries, "=", "\"");
        this.username = (String)headerMap.get("username");
        this.realm = (String)headerMap.get("realm");
        this.nonce = (String)headerMap.get("nonce");
        this.uri = (String)headerMap.get("uri");
        this.response = (String)headerMap.get("response");
        this.qop = (String)headerMap.get("qop");
        this.nc = (String)headerMap.get("nc");
        this.cnonce = (String)headerMap.get("cnonce");
        DigestAuthenticationFilter.logger.debug(LogMessage.format("Extracted username: '%s'; realm: '%s'; nonce: '%s'; uri: '%s'; response: '%s'", new Object[]{this.username, this.realm, this.nonce, this.uri, this.response}));
    }
   //验证和解密
    void validateAndDecode(String entryPointKey, String expectedRealm) throws BadCredentialsException {
        if (this.username != null && this.realm != null && this.nonce != null && this.uri != null && this.response != null) {
            if ("auth".equals(this.qop) && (this.nc == null || this.cnonce == null)) {
                DigestAuthenticationFilter.logger.debug(LogMessage.format("extracted nc: '%s'; cnonce: '%s'", this.nc, this.cnonce));
                throw new BadCredentialsException(DigestAuthenticationFilter.this.messages.getMessage("DigestAuthenticationFilter.missingAuth", new Object[]{this.section212response}, "Missing mandatory digest value; received header {0}"));
            } else if (!expectedRealm.equals(this.realm)) {
                throw new BadCredentialsException(DigestAuthenticationFilter.this.messages.getMessage("DigestAuthenticationFilter.incorrectRealm", new Object[]{this.realm, expectedRealm}, "Response realm name '{0}' does not match system realm name of '{1}'"));
            } else {
                byte[] nonceBytes;
                try {
                    nonceBytes = Base64.getDecoder().decode(this.nonce.getBytes());
                } catch (IllegalArgumentException var8) {
                    throw new BadCredentialsException(DigestAuthenticationFilter.this.messages.getMessage("DigestAuthenticationFilter.nonceEncoding", new Object[]{this.nonce}, "Nonce is not encoded in Base64; received nonce {0}"));
                }

                String nonceAsPlainText = new String(nonceBytes);
                String[] nonceTokens = StringUtils.delimitedListToStringArray(nonceAsPlainText, ":");
                if (nonceTokens.length != 2) {
                    throw new BadCredentialsException(DigestAuthenticationFilter.this.messages.getMessage("DigestAuthenticationFilter.nonceNotTwoTokens", new Object[]{nonceAsPlainText}, "Nonce should have yielded two tokens but was {0}"));
                } else {
                    try {
                        this.nonceExpiryTime = new Long(nonceTokens[0]);
                    } catch (NumberFormatException var7) {
                        throw new BadCredentialsException(DigestAuthenticationFilter.this.messages.getMessage("DigestAuthenticationFilter.nonceNotNumeric", new Object[]{nonceAsPlainText}, "Nonce token should have yielded a numeric first token, but was {0}"));
                    }

                    String expectedNonceSignature = DigestAuthUtils.md5Hex(this.nonceExpiryTime + ":" + entryPointKey);
                    if (!expectedNonceSignature.equals(nonceTokens[1])) {
                        throw new BadCredentialsException(DigestAuthenticationFilter.this.messages.getMessage("DigestAuthenticationFilter.nonceCompromised", new Object[]{nonceAsPlainText}, "Nonce token compromised {0}"));
                    }
                }
            }
        } else {
            throw new BadCredentialsException(DigestAuthenticationFilter.this.messages.getMessage("DigestAuthenticationFilter.missingMandatory", new Object[]{this.section212response}, "Missing mandatory digest value; received header {0}"));
        }
    }
//计算服务摘要
    String calculateServerDigest(String password, String httpMethod) {
    	//生产摘要
        return DigestAuthUtils.generateDigest(DigestAuthenticationFilter.this.passwordAlreadyEncoded, this.username, this.realm, password, httpMethod, this.uri, this.qop, this.nonce, this.nc, this.cnonce);
    }
//判断随机数是否到期
    boolean isNonceExpired() {
        long now = System.currentTimeMillis();
        return this.nonceExpiryTime < now;
    }

    String getUsername() {
        return this.username;
    }

    String getResponse() {
        return this.response;
    }
}

到此这篇关于Spring Security实现HTTP认证的文章就介绍到这了,更多相关Spring Security HTTP认证内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • SpringSecurity如何实现配置单个HttpSecurity

    一.创建项目并导入依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <art

  • 详解spring security之httpSecurity使用示例

    httpSecurity 类似于spring security的xml配置文件命名空间配置中的<http>元素.它允许对特定的http请求基于安全考虑进行配置.默认情况下,适用于所有的请求,但可以使用requestMatcher(RequestMatcher)或者其它相似的方法进行限制. 使用示例: 最基本的基于表单的配置如下.该配置将所有的url访问权限设定为角色名称为"ROLE_USER".同时也定义了内存认证模式:使用用户名"user"和密码&qu

  • springsecurity中http.permitall与web.ignoring的区别说明

    目录 springsecurity http.permitAll与web.ignoring的区别 web.ignoring()与permitAll 总结 springsecurity http.permitAll与web.ignoring的区别 这两个都是继承WebSecurityConfigurerAdapter后重写的方法 http.permitAll不会绕开springsecurity验证,相当于是允许该路径通过 web.ignoring是直接绕开spring security的所有fil

  • 详解Spring Security中的HttpBasic登录验证模式

    一.HttpBasic模式的应用场景 HttpBasic登录验证模式是Spring Security实现登录验证最简单的一种方式,也可以说是最简陋的一种方式.它的目的并不是保障登录验证的绝对安全,而是提供一种"防君子不防小人"的登录验证. 就好像是我小时候写日记,都买一个带小锁头的日记本,实际上这个小锁头有什么用呢?如果真正想看的人用一根钉子都能撬开.它的作用就是:某天你的父母想偷看你的日记,拿出来一看还带把锁,那就算了吧,怪麻烦的. 举一个我使用HttpBasic模式的进行登录验证的

  • 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="

  • Springboot集成Spring Security实现JWT认证的步骤详解

    1 简介 Spring Security作为成熟且强大的安全框架,得到许多大厂的青睐.而作为前后端分离的SSO方案,JWT也在许多项目中应用.本文将介绍如何通过Spring Security实现JWT认证. 用户与服务器交互大概如下: 客户端获取JWT,一般通过POST方法把用户名/密码传给server: 服务端接收到客户端的请求后,会检验用户名/密码是否正确,如果正确则生成JWT并返回:不正确则返回错误: 客户端拿到JWT后,在有效期内都可以通过JWT来访问资源了,一般把JWT放在请求头:一次

  • Springboot WebFlux集成Spring Security实现JWT认证的示例

    1 简介 在之前的文章<Springboot集成Spring Security实现JWT认证>讲解了如何在传统的Web项目中整合Spring Security和JWT,今天我们讲解如何在响应式WebFlux项目中整合.二者大体是相同的,主要区别在于Reactive WebFlux与传统Web的区别. 2 项目整合 引入必要的依赖: <dependency> <groupId>org.springframework.boot</groupId> <art

  • Spring Security+JWT实现认证与授权的实现

    目录 一.登录校验流程 1.Spring Security 完整流程 2.Spring Security的默认登陆验证流程. 3. 整合JWT大致流程 前端响应类 JWT工具类 重写UserDetailsService的方法 重写登录接口 认证过滤器 退出登陆 授权基本流程 限制访问资源所需权限 封装权限信息 RBAC权限模型 自定义失败处理 认证:验证当前访问系统的是不是本系统的用户,并且要确认具体是哪个用户 授权:经过认证后判断当前用户是否有权限进行某个操作 一.登录校验流程 1.Sprin

  • Spring Security实现HTTP认证

    目录 前言 一.HTTP基本认证是什么? 二.HTTP基本认证流程 一.Spring Security使用HTTP基本认证 1.创建项目spring-security-http-auth 2.创建配置文件WebSecurityConfig 3.运行项目 二.Spring Security HTTP基本认证原理 三.HTTP摘要认证是什么? 四.Spring Security使用HTTP摘要认证流程? 运行项目 Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安

  • Spring Security获取用户认证信息的实现流程

    目录 登录用户数据获取 SecurityContextHolder SecurityContextHolderStrategy 多线程情况下获取用户数据 页面上获取用户信息 登录用户数据获取 SecurityContextHolder ​ Spring Security 会将登录用户数据保存在 Session 中.但是,为了使用方便,Spring Security在此基础上还做了一些改进,其中最主要的一个变化就是线程绑定.当用户登录成功后,Spring Security 会将登录成功的用户信息保

  • Spring security自定义用户认证流程详解

    1.自定义登录页面 (1)首先在static目录下面创建login.html 注意:springboot项目默认可以访问resources/resources,resources/staic,resources/public目录下面的静态文件 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>登录页面</titl

  • 详解最简单易懂的Spring Security 身份认证流程讲解

    最简单易懂的Spring Security 身份认证流程讲解 导言 相信大伙对Spring Security这个框架又爱又恨,爱它的强大,恨它的繁琐,其实这是一个误区,Spring Security确实非常繁琐,繁琐到让人生厌.讨厌也木有办法呀,作为JavaEE的工程师们还是要面对的,在开始之前,先打一下比方(比方好可怜): Spring Security 就像一个行政服务中心,如果我们去里面办事,可以办啥事呢?可以小到咨询简单问题.查询社保信息,也可以户籍登记.补办身份证,同样也可以大到企业事

  • Java Spring Security认证与授权及注销和权限控制篇综合解析

    Spring Security简介: Spring Security 是针对Spring项目的安全框架,也是Spring Boot底层安全模块默认的技术选型,它可以实现强大的Web安全控制,对于安全控制,我们只需要引入 spring-boot-starter-security 模块,进行少量的配置,即可实现强大的安全管理! 记住几个类: WebSecurityConfigurerAdapter:自定义Security策略 AuthenticationManagerBuilder:自定义认证策略

  • Spring Security使用数据库登录认证授权

    目录 一.搭建项目环境 1.创建 RBAC五张表 2.创建项目 二.整合 Spring Security实现用户认证 1.后端整合 2.前端整合 三.整合 Spring Security实现用户授权 1.后端 2.前端 一.搭建项目环境 1.创建 RBAC五张表 RBAC,即基于角色的权限访问控制(Role-Based Access Control),就是用户通过角色与权限进行关联.在这种模型中,用户与角色之间,角色与权限之间,一般者是多对多的关系. 在 MySQL数据库中,创建如下几个表: D

随机推荐