Spring Security 框架

这篇具有很好参考价值的文章主要介绍了Spring Security 框架。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

引入依赖

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

基本原理

认证链

Spring Security的认证处理链是一系列的过滤器链,用于处理用户的身份验证和授权操作。这些过滤器在请求处理过程中依次执行,并在不同的阶段进行不同的认证和授权操作,以确保应用程序的安全性和完整性。

下面是Spring Security的标准认证处理链:

  1. UsernamePasswordAuthenticationFilter:该过滤器用于获取用户输入的用户名和密码,并将其封装在UsernamePasswordAuthenticationToken对象中。它还检查用户输入的凭据是否正确,并将其发送到AuthenticationManager进行身份验证。
  2. BasicAuthenticationFilter:该过滤器用于处理基本认证,即通过HTTP消息头传递的用户名和密码。在进行基本认证时,前端应用程序会将用户凭据(base64编码的)添加到HTTP请求标头中。BasicAuthenticationFilter在此通过解码这些凭据并将其发送到AuthenticationManager进行身份验证。
  3. SecurityContextHolderAwareRequestFilter:该过滤器用于包装HttpServletRequest对象,以便将其传递到Spring Security的AuthenticationAwareWebInvocationPrivilegeEvaluator。这主要是用于在页面或JavaScript中检查是否已经经过身份验证。
  4. AnonymousAuthenticationFilter:该过滤器负责提供匿名身份验证机制。如果用户未经过身份验证,则该过滤器会向SecurityContext注入一个AnonymousAuthenticationToken对象。
  5. SessionManagementFilter:该过滤器管理用户的会话,包括创建新的会话、检查会话是否过期以及将会话绑定到用户的身份验证,以防止会话劫持攻击。
  6. ExceptionTranslationFilter:该过滤器处理由其他过滤器抛出的异常。它查找最合适的异常处理程序,例如包含特定响应头和状态代码的RESTful API异常处理程序或进行页面重定向的MVC样式的异常处理程序。
  7. FilterSecurityInterceptor:该过滤器在处理请求之前检查当前用户是否具有所请求的资源的访问权限。如果用户没有权限,则FilterSecurityInterceptor将阻止请求并返回HTTP 403 Forbidden响应。

请求 —》UsernamePasswordAuthenticationFilter—》 … —》ExceptionTranslationFilter —》FilterSecurityInterceptor —》API —》响应 —》FilterSecurityInterceptor —》…(原路返回)

SecurityConfig (WebSecurityConfigurerAdapter)

package com.fanchen.config;

import com.fanchen.security.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import javax.annotation.Resource;

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Resource
    private LoginFailHandler loginFailHandler;

    @Resource
    private LoginSuccessHandler loginSuccessHandler;

    @Resource
    private CaptchaFilter captchaFilter;

    @Resource
    private JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;

    @Resource
    private JwtAccessDeniedHandler jwtAccessDeniedHandler;

    @Resource
    private UserDetailServiceImpl userDetailService;

    @Resource
    private JwtLogoutSuccessHandler jwtLogoutSuccessHandler;

    @Bean
    public JwtAuthenticationFilter jwtAuthenticationFilter() throws Exception {
        return new JwtAuthenticationFilter(authenticationManager());
    }

    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder(){
        return new BCryptPasswordEncoder();
    }
    
    //白名单
    private static final String[] URL_WHITELIST = {
            "/login",
            "/register",
            "/captcha",
            "/register/deptList",
            "/favicon.ico",
    };

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        
        http.cors().and().csrf().disable()
        //登录配置
        .formLogin()
                .successHandler(loginSuccessHandler)
                .failureHandler(loginFailHandler)
        .and()
                .logout()
                .logoutSuccessHandler(jwtLogoutSuccessHandler)
                
        //禁用Session
        .and()
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
        //配置拦截器
        //anonymous() 允许匿名用户访问,不允许已登入用户访问
        //permitAll() 不管登入,不登入 都能访问
        .and()
                .authorizeRequests()
                .antMatchers(URL_WHITELIST).anonymous()
                .antMatchers(
                        HttpMethod.GET,
                        "/",
                        "/*.html",
                        "//*.html",
                        "//*.css",
                        "//*.js",
                        "/img/"
                ).permitAll()
                .antMatchers("/swagger-ui.html").anonymous()
                .antMatchers("/swagger-resources/").anonymous()
                .antMatchers("/webjars/").anonymous()
                .antMatchers("/*/api-docs").anonymous()
                .antMatchers("/druid/**").anonymous()
                .anyRequest().authenticated()
                .and()
                .headers().frameOptions().disable()
        //异常处理器
        .and()
                .exceptionHandling()
                .authenticationEntryPoint(jwtAuthenticationEntryPoint)
                .accessDeniedHandler(jwtAccessDeniedHandler)
        //配置自定义过滤器
        .and()
        .addFilter(jwtAuthenticationFilter())
        .addFilterBefore(captchaFilter, UsernamePasswordAuthenticationFilter.class)
        ;
    }

    //配置userSecvice
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailService);
    }

}

AccessDeniedHandler(处理访问权限不足的处理器)

@Slf4j
@Component
public class JwtAccessDeniedHandler implements AccessDeniedHandler {
    @Override
    public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException {
        log.info("访问受限资源! 执行handle方法:");
        httpServletResponse.setContentType("application/json;charset=utf-8");
        httpServletResponse.setStatus(HttpServletResponse.SC_FORBIDDEN);
        ServletOutputStream outputStream = httpServletResponse.getOutputStream();
        Result fail = Result.fail(e.getMessage());
        outputStream.write(JSONUtil.toJsonStr(fail).getBytes(StandardCharsets.UTF_8));
        outputStream.flush();
        outputStream.close();
    }
}

AuthenticationEntryPoint(处理访问未认证的逻辑)

commence

package com.fanchen.security;

import cn.hutool.json.JSONUtil;
import com.fanchen.common.lang.Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;

import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;


/**
 * @description  认证失败的处理逻辑
 * @author Zhang Guangyun
 * @date 2023/4/23 20:23
 */
@Slf4j
@Component
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {

    @Override
    public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
        log.info("认证失败,执行commence方法");
        httpServletResponse.setContentType("application/json;charset=utf-8");
        httpServletResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
        ServletOutputStream outputStream = httpServletResponse.getOutputStream();
        Result fail = Result.fail(401, "请先登录", null);
        outputStream.write(JSONUtil.toJsonStr(fail).getBytes(StandardCharsets.UTF_8));
        log.info("outputStream = {}",JSONUtil.toJsonStr(fail).getBytes(StandardCharsets.UTF_8));
        outputStream.flush();
        outputStream.close();
    }
}

AuthenticationEntryPoint 与 AccessDeniedHandler有何不同?
AuthenticationEntryPoint和AccessDeniedHandler都是Spring Security处理安全相关问题的接口,但它们处理的问题不同,有以下几个不同点:
调用时机不同:AuthenticationEntryPoint是在用户请求需要身份验证的资源而没有携带认证信息时被触发,而AccessDeniedHandler是在用户尝试访问需要授权的资源但是因为权限不足而被拒绝时被触发。
负责的任务不同:AuthenticationEntryPoint主要负责为用户提供身份验证机会,返回401 Unauthorized错误页面或重定向到登录页面等信息,使用户进行身份验证并重新尝试访问资源。而AccessDeniedHandler主要负责为用户提供授权机会,返回403 Forbidden错误页面或自定义授权错误信息等信息,使用户及时提交授权申请或提供正确的授权信息。
方法参数不同:AuthenticationEntryPoint的commence()方法将从用户请求中提取AuthenticationException,而AccessDeniedHandler的handle()方法将从请求中提取AccessDeniedException。
代码实现不同:在SecurityConfig中配置时,AuthenticationEntryPoint通常使用exceptionHandling()来配置,而AccessDeniedHandler通常使用AccessDecisionManager()来配置。

LogoutSuccessHandler(处理登出成功处理器)

package com.fanchen.security;

import cn.hutool.json.JSONUtil;
import com.fanchen.common.lang.Result;
import com.fanchen.utils.JwtUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
@Slf4j
@Component
public class JwtLogoutSuccessHandler implements LogoutSuccessHandler {

    @Resource
    private JwtUtil jwtUtil;

    /**
     * @description 登出成功的处理逻辑
     * @author Zhang Guangyun
     * @date 2023/4/23 20:30
     * @param httpServletRequest
     * @param httpServletResponse
     * @param authentication
     *  SecurityContextHolder 中清除 当前用户的认证信息(Authentication 对象)
     */
    @Override
    public void onLogoutSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {

        log.info("正在执行  onLogoutSuccess  方法:");
        if (authentication != null){
            new SecurityContextLogoutHandler().logout(httpServletRequest, httpServletResponse, authentication);
        }
        httpServletResponse.setContentType("application/json;charset=utf-8");
        httpServletResponse.setHeader(jwtUtil.getHeader(), "");
        ServletOutputStream outputStream = httpServletResponse.getOutputStream();
        Result success = Result.succ("退出成功");
        outputStream.write(JSONUtil.toJsonStr(success).getBytes(StandardCharsets.UTF_8));
        outputStream.flush();
        outputStream.close();
    }
}

AuthenticationSuccessHandler(处理登入成功逻辑)

onAuthenticationSuccess

package com.fanchen.security;

import cn.hutool.json.JSONUtil;
import com.fanchen.common.lang.Result;
import com.fanchen.utils.AsyncTaskUtil;
import com.fanchen.utils.JwtUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Date;

/**
 * @description  Spring Security 下的 登录
 * @author Zhang Guangyun
 * @date 2023/4/23 15:50
 */
@Slf4j
@Component
public class LoginSuccessHandler implements AuthenticationSuccessHandler {

    @Resource
    private JwtUtil jwtUtil;

    @Resource
    private AsyncTaskUtil asyncTaskUtil;

    @Override
    public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
        log.info("正在执行 onAuthenticationSuccess 方法:");
        httpServletResponse.setContentType("application/json;charset=utf-8");
        String jwt = jwtUtil.createToken(authentication.getName());
        log.info("jwt  =  {}",jwt);
        httpServletResponse.setHeader(jwtUtil.getHeader(), jwt);
        ServletOutputStream outputStream = httpServletResponse.getOutputStream();
        Result success = Result.succ("登录成功");
        log.info("**************登录成功********************");
        outputStream.write(JSONUtil.toJsonStr(success).getBytes(StandardCharsets.UTF_8));
        outputStream.flush();
        outputStream.close();
        asyncTaskUtil.recordLoginInfo(authentication.getName(), 1, "登陆成功", new Date());
    }
}

AuthenticationFailureHandler(处理登录失败的逻辑)

onAuthenticationFailure

package com.fanchen.security;

import cn.hutool.json.JSONUtil;
import com.fanchen.common.lang.Result;
import com.fanchen.utils.AsyncTaskUtil;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Date;

@Component
public class LoginFailHandler implements AuthenticationFailureHandler {

    @Resource
    private AsyncTaskUtil asyncTaskUtil;

    @Override
    public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
        httpServletResponse.setContentType("application/json;charset=utf-8");
        ServletOutputStream outputStream = httpServletResponse.getOutputStream();
        String message = e.getMessage();
        if (!"验证码错误".equals(message) && !"请刷新验证码".equals(message)){
            message = "用户名或密码错误";
        }
        Result fail = Result.fail(message);
        outputStream.write(JSONUtil.toJsonStr(fail).getBytes(StandardCharsets.UTF_8));
        outputStream.flush();
        outputStream.close();
        if (message.equals("请刷新验证码")){
            return;
        }
        asyncTaskUtil.recordLoginInfo(null, 0, message, new Date());
    }
}

UserDetails

AccountUser(记录用户的特征)

记录用户的特征:用户名,密码,

package com.fanchen.security;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.util.Assert;

import java.util.Collection;

/**
 * @description UserDetails对象
 * @author Zhang Guangyun
 */
public class AccountUser implements UserDetails {

    private Long userId;

    private String password;

    private final String username;

    private final Collection<? extends GrantedAuthority> authorities;

    private final boolean accountNonExpired;

    private final boolean accountNonLocked;

    private final boolean credentialsNonExpired;

    private final boolean enabled;

    public AccountUser(Long userId, String username, String password, Collection<? extends GrantedAuthority> authorities) {
        this(userId, username, password, true, true, true, true, authorities);
    }

    /**
     * @description user 的对象(*)
     * @author Zhang Guangyun
     * @date 2023/4/23 20:20
     * @param userId
     * @param username
     * @param password
     * @param enabled
     * @param accountNonExpired
     * @param credentialsNonExpired
     * @param accountNonLocked
     * @param authorities
     */
    public AccountUser(Long userId, String username, String password, boolean enabled, boolean accountNonExpired,
                       boolean credentialsNonExpired, boolean accountNonLocked,
                       Collection<? extends GrantedAuthority> authorities) {
        Assert.isTrue(username != null && !"".equals(username) && password != null,
                "无法将null或空值传递给构造函数");
        this.userId = userId;
        this.username = username;
        this.password = password;
        this.enabled = enabled;
        this.accountNonExpired = accountNonExpired;
        this.credentialsNonExpired = credentialsNonExpired;
        this.accountNonLocked = accountNonLocked;
        this.authorities = authorities;
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return this.authorities;
    }

    @Override
    public String getPassword() {
        return this.password;
    }

    @Override
    public String getUsername() {
        return this.username;
    }

    @Override
    public boolean isAccountNonExpired() {
        return this.accountNonExpired;
    }

    @Override
    public boolean isAccountNonLocked() {
        return this.accountNonLocked;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return this.credentialsNonExpired;
    }

    @Override
    public boolean isEnabled() {
        return this.enabled;
    }
}

UserDetailsService

UserDetailServiceImpl

package com.fanchen.security;

import com.fanchen.entity.SysUser;
import com.fanchen.service.SysUserService;
import com.fanchen.utils.RedisUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.List;

/**
 * @description
 * @author Zhang Guangyun
 * @date 2023/4/23 16:14
 * 在 Spring Security 中,UserDetailsService 接口用于加载用户信息。它是一个核心接口,用于将用户的信息从持久化存储(如数据库、LDAP 等)中加载到内存中,
 * 供 Spring Security 使用。在用户登录时,Spring Security 会使用 UserDetailsService 加载用户信息,并将其封装到 Authentication 对象中,以进行后续的身份认证和授权操作。
 */
@Slf4j
@Service
public class UserDetailServiceImpl implements UserDetailsService {

    @Resource
    private SysUserService sysUserService;

    @Resource
    private RedisUtil redisUtil;

    @Override
    public UserDetails loadUserByUsername(String username) {
        log.info("执行loadUserByUsername方法,username = {}",username);
        SysUser sysUser = sysUserService.getByUsername(username);
        if (sysUser == null) {
            throw new UsernameNotFoundException("用户名或密码错误");
        }
        //在redis注册登录用户
        redisUtil.set("User:" + username, sysUser, 3700);

        AccountUser user = new AccountUser(sysUser.getId(), sysUser.getUsername(), sysUser.getPassword(), null);

        return user;
    }


    /**
     * 用户权限信息(角色,菜单权限)
     * @param username 用户id
     * @return 权限列表
     */
    public List<GrantedAuthority> getUserAuthority(String username){
        String authority = sysUserService.getUserAuthority(username);
        log.info("authority = {}",authority);
        //将,分割的用户 权限 变成 list类型 返回回来
        List<GrantedAuthority> grantedAuthorityList = AuthorityUtils.commaSeparatedStringToAuthorityList(authority);
        return grantedAuthorityList;
    }
}

BasicAuthenticationFilter

doFilterInternal
Spring Security 中的 BasicAuthenticationFilter 是一个过滤器,用于处理 HTTP 基本身份验证。在执行身份验证之前,它会检查请求中是否包含 Authorization 头,以确定是否需要进行身份验证。如果请求需要身份验证,则 BasicAuthenticationFilter 从 Authorization 头中获取凭证,并使用 Spring Security 中的 AuthenticationManager 进行身份验证。

doFilterInternal() 是 BasicAuthenticationFilter 的主要方法,它实现了 Filter 接口定义的方法,用于处理请求。在 doFilterInternal() 方法中,它首先检查请求中是否包含了 Authorization 头,如果没有 Authorization 头,那么它会检查是否已经设置了 SecurityContextHolder 中的 Authentication 对象,如果已经设置了,则直接放行,让请求继续被处理。如果没有设置 Authentication 对象,则返回一个 401 无权限响应。
如果请求中包含了 Authorization 头,则 BasicAuthenticationFilter 会解析 Authorization 头,提取出 Base64 编码的用户名和密码,并使用 AuthenticationManager 进行身份验证。如果身份验证成功,则BasicAuthenticationFilter 根据用户名和密码创建一个 UsernamePasswordAuthenticationToken 对象,并将其设置为 SecurityContextHolder 中的 Authentication 对象。然后请求被放行,并继续被其他过滤器或 Spring Security 的安全配置所处理。
如果身份验证失败,则 BasicAuthenticationFilter 将返回一个 401 无权限响应,请求不会被进一步处理。

package com.fanchen.security;

import cn.hutool.core.util.StrUtil;
import com.fanchen.utils.JwtUtil;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;

import javax.annotation.Resource;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Slf4j
public class JwtAuthenticationFilter extends BasicAuthenticationFilter {

    @Resource
    private JwtUtil jwtUtil;

    @Resource
    private UserDetailServiceImpl userDetailService;

    public JwtAuthenticationFilter(AuthenticationManager authenticationManager) {
        super(authenticationManager);
    }


    /**
     *BasicAuthenticationFilter 是 Spring Security 提供的一个过滤器,用于处理 HTTP Basic 认证方式的请求。在客户端发送 HTTP 请求时,如果请求头中包含了 Authorization 字段,那么服务器端就可以通过该字段中携带的用户名和密码来进行认证。BasicAuthenticationFilter 类就是用来处理这种认证方式的。
     * BasicAuthenticationFilter 类继承自 OncePerRequestFilter 类,实现了其中的 doFilterInternal() 方法。在该方法中,它会首先判断当前请求是否包含 Authorization 请求头,如果没有,则直接调用 FilterChain 对象的 doFilter() 方法将请求传递给下一个过滤器或 servlet。如果请求头中包含了 Authorization 字段,则会对其中的用户名和密码进行解析并进行认证。具体的流程如下:
     * 获取请求头中的 Authorization 字段,并从中提取出用户名和密码。
     * 将提取出的用户名和密码封装到一个 UsernamePasswordAuthenticationToken 对象中。
     * 调用 AuthenticationManager 对象的 authenticate() 方法对该 UsernamePasswordAuthenticationToken 进行认证。
     * 如果认证成功,则将认证信息保存到 SecurityContextHolder 中,并调用 FilterChain 对象的 doFilter() 方法将请求传递给下一个过滤器或 servlet。如果认证失败,则会调用 AuthenticationEntryPoint 对象的 commence() 方法返回认证失败的响应。
     */
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
        log.info("执行doFilterInternal 方法: ");

        String jwt = request.getHeader(jwtUtil.getHeader());
        //如果jwt 是空白 或者 undefinded
        if (StrUtil.isBlankOrUndefined(jwt)){
            chain.doFilter(request, response);
            return;
        }
        //解析token
        Claims claims = jwtUtil.parserToken(jwt);

        if (claims == null){
            throw new JwtException("token异常");
        }

        if (jwtUtil.isExpire(claims)){
            throw new JwtException("token过期");
        }

        String username = claims.getSubject();
        UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, null,
                                                             userDetailService.getUserAuthority(username));
        SecurityContextHolder.getContext().setAuthentication(token);

        chain.doFilter(request, response);
    }
}

UsernamePasswordAuthenticationToken 对象

在 Spring Security 中,UsernamePasswordAuthenticationToken 对象是一种用于简单认证的身份验证对象。Spring Security 支持各种身份验证机制,并提供了可以在这些机制中使用的通用身份验证对象。其中包括 UsernamePasswordAuthenticationToken 对象。
UsernamePasswordAuthenticationToken 对象可以用于包装用户名和密码等凭据,并将这些凭据传递给进行身份验证的 AuthenticationManager。如果身份验证成功,则 UsernamePasswordAuthenticationToken 对象将包含一个认证主体,其中包含关于已认证用户的信息,该对象将被存储在 SecurityContextHolder 中。
在大多数情况下,UsernamePasswordAuthenticationToken 对象的 credentials 属性具有密码(凭证),principal 属性具有用户名或身份识别值。在进行身份验证后,这些凭据被移除。
以下是一个示例代码,说明如何创建一个

UsernamePasswordAuthenticationToken 对象:
Authentication token = new UsernamePasswordAuthenticationToken(username, password);
Authentication result = authenticationManager.authenticate(token);
SecurityContextHolder.getContext().setAuthentication(result);

在该示例中,我们首先创建一个 UsernamePasswordAuthenticationToken 对象,并将用户名和密码作为参数传入构造函数。然后使用 AuthenticationManager 对象调用 authenticate() 方法来进行身份验证。如果验证成功,就会返回一个 Authentication 对象,该对象将包含有关已经验证的用户的信息。最后,使用 SecurityContextHolder.getContext().setAuthentication() 方法设置该身份验证信息,以便它可以在整个应用程序的生命周期内访问。

OncePerRequestFilter (自定义过滤器)

OncePerRequestFilter是Spring Security框架中的一个抽象类,实现了javax.servlet.Filter接口,通过继承它可以方便地实现对请求的过滤。它的作用是确保在请求处理期间只被调用一次,即只会过滤一次该请求,可以避免同一请求被重复处理的问题。

OncePerRequestFilter实现了doFilter()方法,该方法在每个请求到达过滤器时会被调用。通过继承该类并重写方法,可以自定义实现请求过滤器的逻辑。一般来说我们在该过滤器中可以对请求进行处理,例如:文章来源地址https://www.toymoban.com/news/detail-480732.html

  1. 鉴权:检查请求中是否有合法的认证信息,如果没有,可以拦截请求并返回错误信息或者重定向到登录页面等操作,确保资源得到保护。
  2. 日志:记录请求的详细信息,例如请求头、URL、请求参数等,方便后续的审计和追踪分析。
  3. 缓存:根据请求的不同参数将相应的结果进行缓存,减轻服务器的负担,提高性能。
    OncePerRequestFilter通常被用来实现我们自定义的请求过滤逻辑,比如对请求进行鉴权、日志记录、缓存等操作。我们可以继承该类,并重写其中的doFilterInternal方法,实现我们自己的处理逻辑。该方法会在每个请求到达过滤器时被调用,由于OncePerRequestFilter的存在,我们可以确保每个请求只被过滤一次,避免重复处理导致性能问题的发生。
package com.fanchen.security;

import com.fanchen.common.exception.CaptchaException;
import com.fanchen.common.lang.Const;
import com.fanchen.utils.RedisUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.annotation.Resource;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @description  验证码
 * @author Zhang Guangyun
 * @date 2023/5/27 15:54
 */
@Component
public class CaptchaFilter extends OncePerRequestFilter {

    @Resource
    private RedisUtil redisUtil;

    @Resource
    private LoginFailHandler loginFailHandler;

    @Override
    protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
        String url = httpServletRequest.getRequestURI();
        if (("/login".equals(url) && httpServletRequest.getMethod().equals("POST")) || ("/register".equals(url) && httpServletRequest.getMethod().equals("POST"))){
            try {
                validate(httpServletRequest);
            }catch (CaptchaException captchaException){
                loginFailHandler.onAuthenticationFailure(httpServletRequest, httpServletResponse, captchaException);
                return;
            }
        }
        doFilter(httpServletRequest, httpServletResponse, filterChain);
    }

    private void validate(HttpServletRequest request) {
        String code = request.getParameter("code").toLowerCase();
        String key = request.getParameter("key");
        if (StringUtils.isBlank(code) || StringUtils.isBlank(key)) {
            throw new CaptchaException("验证码错误");
        }
        Object hget = redisUtil.hget(Const.CAPTCHA_KEY, key);
        if (hget == null || hget.equals("")){
            throw new CaptchaException("请刷新验证码");
        }
        if (!code.equals(hget.toString())) {
            throw new CaptchaException("验证码错误");
        }
        redisUtil.hdel(Const.CAPTCHA_KEY, key);
    }
}

到了这里,关于Spring Security 框架的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Spring Security(安全框架)

    (1)Spring Security 是一个高度自定义的 安全框架 。利用Spring IoC/DI和AOP功能,为系统提供了声明式安全访问控制功能,减少了为系统安全而编写大量重复代码的工作。 (2)认证(Authentication): 应用程序确认用户身份的过程,常见认证:登录。 (3)身份(principal)/主体(

    2023年04月13日
    浏览(61)
  • Spring Security 框架详解

    Spring Security是一款基于Spring框架的认证和授权框架,提供了一系列控制访问和保护应用程序的功能,同时也支持基于角色和权限的访问控制,加密密码,CSRF防范,会话管理等多种功能。Spring Security可以轻松地与其他Spring框架,如Spring Boot和Spring MVC进行集成使用。 本文将会对

    2024年02月04日
    浏览(29)
  • SpringBoot——Spring Security 框架

    优质博文:IT-BLOG-CN Spring Security 是一个能够为基于 Spring 的企业应用系统提供声明式的安全访问控制解决方案的安全框架。它提供了一组可以在 Spring 应用上下文中配置的 Bean,充分利用了 Spring IoC , DI (控制反转 Inversion of Control , DI : Dependency Injection 依赖注入)和 AOP (面

    2024年02月04日
    浏览(26)
  • [SpringBoot]Spring Security框架

    目录 关于Spring Security框架 Spring Security框架的依赖项 Spring Security框架的典型特征  关于Spring Security的配置 关于默认的登录页 关于请求的授权访问(访问控制)  使用自定义的账号登录 使用数据库中的账号登录 关于密码编码器 使用BCrypt算法 关于伪造的跨域攻击 使用前后端分

    2024年02月08日
    浏览(28)
  • Spring6-IoC(Inversion of Control)控制反转和DI(Dependency Injection)依赖注入,手动实现IOC

    Java 反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为 Java 语言的 反射机制 。简单来说, 反射机制指的是程序在运行时能够获取自身

    2024年02月09日
    浏览(55)
  • 【Spring Security系列】一文带你了解权限框架与Spring Security核心概念

    权限框架是软件开发中用于管理 用户权限和访问控制 的工具。在企业或者我们毕设复杂的系统中,不同的用户或角色需要拥有不同的访问和操作权限,以确保系统的安全性和数据完整性。今天我们就讨论一下Java中的安全框架! 在企业的开发中,Spring Security,Shiro都是比较流

    2024年04月16日
    浏览(32)
  • 【Spring Security】安全框架学习(八)

    3.0 权限系统的作用 例如一个学校图书馆的管理系统,如果是普通学生登录就能看到借书还书相关的功能,不可能让他看到并且去使用添加书籍信息,删除书籍信息等功能。但是如果是一个图书馆管理员的账号登录了,应该就能看到并使用添加书籍信息,删除书籍信息等功能

    2024年02月09日
    浏览(47)
  • 【Spring Security】安全框架学习(十二)

    6.0 其他权限校验方法 我们前面都是使用@PreAuthorize注解,然后在在其中使用的是hasAuthority方法进行校验。Spring Security还为我们提供了其它方法. 例如:hasAnyAuthority,hasRole,hasAnyRole,等。 这里我们先不急着去介绍这些方法,我们先去理解hasAuthority的原理,然后再去学习其他方法就

    2024年02月07日
    浏览(35)
  • 在Spring Boot框架中集成 Spring Security

    技术介绍 SpringSecurity的核心功能: SpringSecurity特点: 具体实现 1、集成依赖 2、修改spring security 实现service.impl.UserDetailsServiceImpl类 代码1具体解释 代码2具体解释 实现config.SecurityConfig类 代码具体解释 总结 Spring Security是一个基于Spring框架的安全性框架,它提供了一系列的安全性

    2024年02月14日
    浏览(40)
  • Shiro和Spring Security安全框架对比

    Apache Shiro是Java的一个安全框架。目前,使用Apache Shiro的人越来越多,因为它相当简单。与Spring Security对比,Shiro可能没有Spring Security做的功能强大,但是在实际工作时可能并不需要那么复杂的东西,所以使用小而简单的Shiro就足够了。下面对这两个安全框架进行了对比,可以

    2024年02月10日
    浏览(29)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包