首页 > 其他分享 >七、Spring Boot集成Spring Security之前后分离认证最佳实现

七、Spring Boot集成Spring Security之前后分离认证最佳实现

时间:2024-11-06 21:32:10浏览次数:3  
标签:web Spring Boot springframework token import org Security security

二、自定义用户名密码认证过滤器RestfulUsernamePasswordAuthenticationFilter

1、注册过滤器方式

  1. 使用httpSecurity.addFilter/addFilterBefore/addFilterAfter向过滤器链中添加过滤器,其中addFilter只能添加内置的过滤器,顺序已在过滤器顺序注册器(FilterOrderRegistration)中设置;addFilterBefore/addFilterAfter可以添加自定义过滤器,添加在指定的过滤器之前/之后。该方式优点是使用简单,缺点是无法使用spring security内置的组件,与RestfulUsernamePasswordAuthenticationFilter需要使用AuthenticationManager组件冲突,故不使用该方式。
  2. 使用SecurityConfigurer通过配置类的方式向过滤器链中添加过滤器,官方使用的方式。该方式优点是可以使用spring security内置的组件,缺点是实现较为笨重,而且只能注册过滤器顺序注册器(FilterOrderRegistration)中设定的过滤器。该方式可以使用spring security内置的组件,所以采用本方式,需要修改过滤器顺序注册器添加自定义的过滤器。

2、修改并覆盖过滤器顺序注册器

  1. FilterOrderRegistration类为final类且未提供开放的注册自定义过滤器的方式,所以只能重写该类,并添加自定义过滤器的顺序
package org.springframework.security.config.annotation.web.builders;

import com.yu.demo.spring.filter.RestfulUsernamePasswordAuthenticationFilter;
import org.springframework.security.web.access.ExceptionTranslationFilter;
import org.springframework.security.web.access.channel.ChannelProcessingFilter;
import org.springframework.security.web.access.intercept.AuthorizationFilter;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.authentication.AnonymousAuthenticationFilter;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.LogoutFilter;
import org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter;
import org.springframework.security.web.authentication.preauth.x509.X509AuthenticationFilter;
import org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter;
import org.springframework.security.web.authentication.switchuser.SwitchUserFilter;
import org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter;
import org.springframework.security.web.authentication.ui.DefaultLogoutPageGeneratingFilter;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.security.web.authentication.www.DigestAuthenticationFilter;
import org.springframework.security.web.context.SecurityContextHolderFilter;
import org.springframework.security.web.context.SecurityContextPersistenceFilter;
import org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter;
import org.springframework.security.web.csrf.CsrfFilter;
import org.springframework.security.web.header.HeaderWriterFilter;
import org.springframework.security.web.jaasapi.JaasApiIntegrationFilter;
import org.springframework.security.web.savedrequest.RequestCacheAwareFilter;
import org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter;
import org.springframework.security.web.session.ConcurrentSessionFilter;
import org.springframework.security.web.session.DisableEncodeUrlFilter;
import org.springframework.security.web.session.ForceEagerSessionCreationFilter;
import org.springframework.security.web.session.SessionManagementFilter;
import org.springframework.web.filter.CorsFilter;

import javax.servlet.Filter;
import java.util.HashMap;
import java.util.Map;

final class FilterOrderRegistration {


    private static final int INITIAL_ORDER = 100;

    private static final int ORDER_STEP = 100;

    private final Map<String, Integer> filterToOrder = new HashMap<>();

    FilterOrderRegistration() {
        Step order = new Step(INITIAL_ORDER, ORDER_STEP);
        put(DisableEncodeUrlFilter.class, order.next());
        put(ForceEagerSessionCreationFilter.class, order.next());
        put(ChannelProcessingFilter.class, order.next());
        order.next(); // gh-8105
        put(WebAsyncManagerIntegrationFilter.class, order.next());
        put(SecurityContextHolderFilter.class, order.next());
        put(SecurityContextPersistenceFilter.class, order.next());
        put(HeaderWriterFilter.class, order.next());
        put(CorsFilter.class, order.next());
        put(CsrfFilter.class, order.next());
        put(LogoutFilter.class, order.next());
        this.filterToOrder.put(
                "org.springframework.security.oauth2.client.web.OAuth2AuthorizationRequestRedirectFilter",
                order.next());
        this.filterToOrder.put(
                "org.springframework.security.saml2.provider.service.servlet.filter.Saml2WebSsoAuthenticationRequestFilter",
                order.next());
        put(X509AuthenticationFilter.class, order.next());
        put(AbstractPreAuthenticatedProcessingFilter.class, order.next());
        this.filterToOrder.put("org.springframework.security.cas.web.CasAuthenticationFilter", order.next());
        this.filterToOrder.put("org.springframework.security.oauth2.client.web.OAuth2LoginAuthenticationFilter",
                order.next());
        this.filterToOrder.put(
                "org.springframework.security.saml2.provider.service.servlet.filter.Saml2WebSsoAuthenticationFilter",
                order.next());
        //添加自定义过滤器
        put(RestfulUsernamePasswordAuthenticationFilter.class, order.next());
        put(UsernamePasswordAuthenticationFilter.class, order.next());
        order.next(); // gh-8105
        this.filterToOrder.put("org.springframework.security.openid.OpenIDAuthenticationFilter", order.next());
        put(DefaultLoginPageGeneratingFilter.class, order.next());
        put(DefaultLogoutPageGeneratingFilter.class, order.next());
        put(ConcurrentSessionFilter.class, order.next());
        put(DigestAuthenticationFilter.class, order.next());
        this.filterToOrder.put(
                "org.springframework.security.oauth2.server.resource.web.BearerTokenAuthenticationFilter",
                order.next());
        put(BasicAuthenticationFilter.class, order.next());
        put(RequestCacheAwareFilter.class, order.next());
        put(SecurityContextHolderAwareRequestFilter.class, order.next());
        put(JaasApiIntegrationFilter.class, order.next());
        put(RememberMeAuthenticationFilter.class, order.next());
        put(AnonymousAuthenticationFilter.class, order.next());
        this.filterToOrder.put("org.springframework.security.oauth2.client.web.OAuth2AuthorizationCodeGrantFilter",
                order.next());
        put(SessionManagementFilter.class, order.next());
        put(ExceptionTranslationFilter.class, order.next());
        put(FilterSecurityInterceptor.class, order.next());
        put(AuthorizationFilter.class, order.next());
        put(SwitchUserFilter.class, order.next());
    }

    /**
     * Register a {@link Filter} with its specific position. If the {@link Filter} was
     * already registered before, the position previously defined is not going to be
     * overriden
     *
     * @param filter   the {@link Filter} to register
     * @param position the position to associate with the {@link Filter}
     */
    void put(Class<? extends Filter> filter, int position) {
        String className = filter.getName();
        if (this.filterToOrder.containsKey(className)) {
            return;
        }
        this.filterToOrder.put(className, position);
    }

    /**
     * Gets the order of a particular {@link Filter} class taking into consideration
     * superclasses.
     *
     * @param clazz the {@link Filter} class to determine the sort order
     * @return the sort order or null if not defined
     */
    Integer getOrder(Class<?> clazz) {
        while (clazz != null) {
            Integer result = this.filterToOrder.get(clazz.getName());
            if (result != null) {
                return result;
            }
            clazz = clazz.getSuperclass();
        }
        return null;
    }

    private static class Step {

        private final int stepSize;
        private int value;

        Step(int initialValue, int stepSize) {
            this.value = initialValue;
            this.stepSize = stepSize;
        }

        int next() {
            int value = this.value;
            this.value += this.stepSize;
            return value;
        }

    }

}

3、创建RestfulUsernamePasswordAuthenticationFilter

  1. 参考UsernamePasswordAuthenticationFilter
  2. 将参数获取方式从request.getParameter改为从body体中
  3. 创建UsernamePasswordAuthenticationToken
  4. 设置细节
  5. 调用getAuthenticationManager()的authenticate方法获取认证信息
package com.yu.demo.spring.filter;

import com.yu.demo.util.SpringUtil;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Map;

/**
 * 自定义前后端分离/restful方式的用户名密码认证过滤器
 * 参考UsernamePasswordAuthenticationFilter
 */
public class RestfulUsernamePasswordAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
    //是否只支持post方法
    private final boolean postOnly;
    private final String username;
    private final String password;

    public RestfulUsernamePasswordAuthenticationFilter(String username, String password, String loginUrl, String httpMethod) {
        super(new AntPathRequestMatcher(loginUrl, httpMethod));
        postOnly = HttpMethod.POST.name().equals(httpMethod);
        this.username = username;
        this.password = password;
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException {
        if (this.postOnly && !request.getMethod().equals(HttpMethod.POST.name())) {
            throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
        } else {
            Map<String, String> body = SpringUtil.rawBodyToMap(request);
            String name = body.get(username);
            String pswd = body.get(password);
            UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(name, pswd);
            setDetails(request, authRequest);
            return getAuthenticationManager().authenticate(authRequest);
        }
    }

    protected void setDetails(HttpServletRequest request, UsernamePasswordAuthenticationToken authRequest) {
        authRequest.setDetails(this.authenticationDetailsSource.buildDetails(request));
    }

}

4、创建自定义用户名密码认证过滤器配置类RestfulLoginConfigurer

  1. 参考FormLoginConfigurer
  2. 注册自定义用户名密码认证过滤器RestfulUsernamePasswordAuthenticationFilter
  3. 设置登录地址和请求方式
package com.yu.demo.spring.filter;

import org.springframework.security.config.annotation.web.HttpSecurityBuilder;
import org.springframework.security.config.annotation.web.configurers.AbstractAuthenticationFilterConfigurer;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.RequestMatcher;

/**
 * 自定义前后端分离/restful方式的用户名密码验证过滤器配置器,用于注册认证过滤器
 * 参考FormLoginConfigurer
 */
public class RestfulLoginConfigurer<H extends HttpSecurityBuilder<H>> extends AbstractAuthenticationFilterConfigurer<H, RestfulLoginConfigurer<H>, RestfulUsernamePasswordAuthenticationFilter> {
    private final String loginMethod;

    public RestfulLoginConfigurer(RestfulUsernamePasswordAuthenticationFilter authenticationFilter, String defaultLoginProcessingUrl, String loginMethod) {
        super(authenticationFilter, defaultLoginProcessingUrl);
        this.loginMethod = loginMethod;
    }

    @Override
    public RestfulLoginConfigurer<H> loginPage(String loginPage) {
        return super.loginPage(loginPage);
    }

    @Override
    public void init(H http) throws Exception {
        super.init(http);
    }

    @Override
    protected RequestMatcher createLoginProcessingUrlMatcher(String loginProcessingUrl) {
        return new AntPathRequestMatcher(loginProcessingUrl, loginMethod);
    }
}

三、自定义安全上下文仓库SecurityContextRepositoryImpl

  1. 基于分布式缓存实现安全上下文仓库
  2. 获取上下文时从请求头中获取token,通过token从缓存中获取上下文,不存在时返回空值安全上下文
  3. 保存上下文时从请求头或者登录用户信息中获取token,将token和上下文保存到缓存中

1、分布式缓存接口和实现

package com.yu.demo.manager;

import org.springframework.security.core.context.SecurityContext;

public interface CacheManager {

    /**
     * 通过token获取认证信息
     *
     * @param token token
     * @return 认证信息
     */
    SecurityContext getSecurityContext(String token);

    /**
     * 是否包含token
     *
     * @param token token
     * @return 是否包含token
     */
    boolean contains(String token);

    /**
     * 通过token添加认证信息
     *
     * @param token           token
     * @param securityContext 认证信息
     */
    void addSecurityContext(String token, SecurityContext securityContext);

    /**
     * 通过token删除认证信息
     *
     * @param token token
     */
    void deleteSecurityContext(String token);

}

为演示方便,这里采用过期Map,实际使用将map改为redis或者其他分布式缓存即可

package com.yu.demo.manager.impl;

import com.yu.demo.manager.CacheManager;
import net.jodah.expiringmap.ExpirationPolicy;
import net.jodah.expiringmap.ExpiringMap;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.util.concurrent.TimeUnit;

@Component
public class CacheManagerImpl implements CacheManager {

    private static ExpiringMap<String, SecurityContext> SECURITY_CONTEXT_CACHE;

    @PostConstruct
    public void init() {
        SECURITY_CONTEXT_CACHE = ExpiringMap.builder().maxSize(200).expiration(30, TimeUnit.MINUTES).expirationPolicy(ExpirationPolicy.ACCESSED).variableExpiration().build();
    }

    @Override
    public SecurityContext getSecurityContext(String token) {
        return SECURITY_CONTEXT_CACHE.get(token);
    }

    @Override
    public boolean contains(String token) {
        return SECURITY_CONTEXT_CACHE.containsKey(token);
    }

    @Override
    public void addSecurityContext(String token, SecurityContext securityContext) {
        SECURITY_CONTEXT_CACHE.put(token, securityContext);
    }

    @Override
    public void deleteSecurityContext(String token) {
        SECURITY_CONTEXT_CACHE.remove(token);
    }
}

2、创建SecurityContextRepositoryImpl

package com.yu.demo.spring.impl;

import com.yu.demo.entity.UserDetailsImpl;
import com.yu.demo.manager.CacheManager;
import com.yu.demo.util.SecurityUtil;
import org.apache.poi.util.StringUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.context.HttpRequestResponseHolder;
import org.springframework.security.web.context.SecurityContextRepository;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Component
public class SecurityContextRepositoryImpl implements SecurityContextRepository {

    private static final String AUTHENTICATION = "Authentication";
    @Autowired
    private CacheManager cacheManager;

    @Override
    public SecurityContext loadContext(HttpRequestResponseHolder requestResponseHolder) {
        //获取请求头中的token,未登录访问系统时Token为空
        String token = requestResponseHolder.getRequest().getHeader(AUTHENTICATION);
        if (StringUtil.isNotBlank(token)) {
            SecurityContext securityContext = cacheManager.getSecurityContext(token);
            //securityContext已过期时为空
            if (SecurityUtil.isNotAuthenticated(securityContext)) {
                return SecurityContextHolder.createEmptyContext();
            }
            UsernamePasswordAuthenticationToken authentication = (UsernamePasswordAuthenticationToken) securityContext.getAuthentication();
            UserDetailsImpl userDetails = (UserDetailsImpl) authentication.getPrincipal();
            if (token.equals(userDetails.getToken())) {
                //测试过程中伪造的Token(不修改header和body,只修改signature部分字符)有概率出现可以解析成功的情况,可能是secret太短的原因,未深究,所以这里在验证下输入的Token和缓存中的token
                return securityContext;
            }
        }
        return SecurityContextHolder.createEmptyContext();
    }

    @Override
    public void saveContext(SecurityContext securityContext, HttpServletRequest request, HttpServletResponse response) {
        //获取请求头中的token(登出时有,登录时没有)
        String token = request.getHeader(AUTHENTICATION);
        UsernamePasswordAuthenticationToken authentication = (UsernamePasswordAuthenticationToken) securityContext.getAuthentication();
        if (StringUtil.isBlank(token) && SecurityUtil.isNotAuthenticated(securityContext)) {
            //未登录、验证码、用户名密码校验失败
            return;
        }
        //第一次登录时Token为空
        if (StringUtil.isBlank(token)) {
            UserDetailsImpl userDetails = (UserDetailsImpl) authentication.getPrincipal();
            //登录成功
            cacheManager.addSecurityContext(userDetails.getToken(), securityContext);
            return;
        }
        //退出或token过期(缓存中设置token过期时间)
        if (SecurityUtil.isNotAuthenticated(securityContext)) {
            cacheManager.deleteSecurityContext(token);
            return;
        }
        //更新Token
        cacheManager.addSecurityContext(token, securityContext);
    }

    @Override
    public boolean containsContext(HttpServletRequest request) {
        //本版本的Spring Security只有SessionManagementFilter中调用该方法
        //已禁用SessionManagementFilter,该方法不会被调用
        String token = request.getHeader(AUTHENTICATION);
        if (StringUtil.isBlank(token)) {
            return false;
        }
        if (StringUtil.isBlank(token)) {
            return false;
        }
        return cacheManager.contains(token);
    }

}

四、自定义用户详情UserDetailsImpl

package com.yu.demo.entity;

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import java.util.Collection;
import java.util.Set;

@Setter
@Getter
@ToString
public class UserDetailsImpl implements UserDetails {
    private String password;
    private final String username;
    private final Set<GrantedAuthority> authorities;
    private final boolean accountNonExpired;
    private final boolean accountNonLocked;
    private final boolean credentialsNonExpired;
    private final boolean enabled;
    /**
     * token
     */
    private String token;

    public UserDetailsImpl(String username, String password, boolean accountNonExpired, boolean credentialsNonExpired, boolean accountNonLocked, boolean enabled, Set<GrantedAuthority> grantedAuthorities) {
        this.username = username;
        this.password = password;
        this.enabled = enabled;
        this.accountNonExpired = accountNonExpired;
        this.credentialsNonExpired = credentialsNonExpired;
        this.accountNonLocked = accountNonLocked;
        this.authorities = grantedAuthorities;
    }

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

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

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

    /**
     * 账号是否未过期
     *
     * @return true:是,false:否
     */
    @Override
    public boolean isAccountNonExpired() {
        return accountNonExpired;
    }

    /**
     * 账号是否未锁定
     *
     * @return true:是,false:否
     */
    @Override
    public boolean isAccountNonLocked() {
        return accountNonLocked;
    }

    /**
     * 密码是否未过期
     *
     * @return true:是,false:否
     */
    @Override
    public boolean isCredentialsNonExpired() {
        return credentialsNonExpired;
    }

    /**
     * 账号是否启用
     *
     * @return true:是,false:否
     */
    @Override
    public boolean isEnabled() {
        return enabled;
    }
}

五、自定义用户详情数据库查询UserDetailsServiceImpl

package com.yu.demo.spring.impl;

import com.yu.demo.entity.UserDetailsImpl;
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 java.util.UUID;

@Service
public class UserDetailsServiceImpl implements UserDetailsService {

    //@Autowired
    //private UserService userService;


    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //TODO 通过username从数据库中获取用户,将用户转UserDetails
        //User user = userService.getByUsername(username);
        //return new User(username, user.getPassword(), user.getEnable(), user.getAccountNonExpired(), user.getCredentialsNonExpired(), user.getAccountNonLocked(), user.getAuthorities());
        //{noop}不使用密码加密器,密码123的都可以验证成功
        UserDetailsImpl userDetails = new UserDetailsImpl(username, "{noop}123", true, true, true, true, null);
        //userDetails中设置token,该token只是实现认证流程,未使用jwt
        userDetails.setToken(UUID.randomUUID().toString());
        return userDetails;
    }

}

六、自定义登出登出结果处理器

package com.yu.demo.spring.impl;


import com.yu.demo.entity.UserDetailsImpl;
import com.yu.demo.util.SpringUtil;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.stereotype.Component;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

@Component
public class LoginResultHandler implements AuthenticationSuccessHandler, LogoutSuccessHandler {

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException {
        UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken = (UsernamePasswordAuthenticationToken) authentication;
        UserDetailsImpl userDetailsImpl = (UserDetailsImpl) usernamePasswordAuthenticationToken.getPrincipal();
        Map<String, Object> resp = new HashMap<>();
        //00000表示成功
        resp.put("code", "00000");
        resp.put("token", userDetailsImpl.getToken());
        //生成token返回到前端
        SpringUtil.respJson(response, resp);
    }

    @Override
    public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        Map<String, Object> resp = new HashMap<>();
        //00000表示成功
        resp.put("code", "00000");
        SpringUtil.respJson(response, resp);
    }
}

七、过滤器链个性化配置

package com.yu.demo.config;

import com.yu.demo.spring.filter.RestfulLoginConfigurer;
import com.yu.demo.spring.filter.RestfulUsernamePasswordAuthenticationFilter;
import com.yu.demo.spring.impl.LoginResultHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
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.configurers.CsrfConfigurer;
import org.springframework.security.config.annotation.web.configurers.FormLoginConfigurer;
import org.springframework.security.config.annotation.web.configurers.HttpBasicConfigurer;
import org.springframework.security.config.annotation.web.configurers.SessionManagementConfigurer;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.context.SecurityContextRepository;

@Configuration
@EnableWebSecurity
public class SecurityConfig {
    //登录参数用户名
    private static final String LOGIN_ARG_USERNAME = "username";
    //登录参数密码
    private static final String LOGIN_ARG_PASSWORD = "password";
    //登录请求类型
    private static final String LOGIN_HTTP_METHOD = HttpMethod.POST.name();
    //登录请求地址
    private static final String LOGIN_URL = "/login";
    //登出请求地址
    private static final String LOGOUT_URL = "/logout";

    @Autowired
    private LoginResultHandler loginResultHandler;
    @Autowired
    private SecurityContextRepository securityContextRepository;

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
        httpSecurity
                //禁用UsernamePasswordAuthenticationFilter、DefaultLoginPageGeneratingFilter、DefaultLogoutPageGeneratingFilter
                .formLogin(FormLoginConfigurer::disable)
                //禁用BasicAuthenticationFilter
                .httpBasic(HttpBasicConfigurer::disable)
                //禁用CsrfFilter
                .csrf(CsrfConfigurer::disable)
                //禁用SessionManagementFilter
                .sessionManagement(SessionManagementConfigurer::disable)
                //http请求认证
                .authorizeHttpRequests(authorizeHttpRequestsCustomizer -> authorizeHttpRequestsCustomizer
                        //任何请求
                        .anyRequest()
                        //需要认证
                        .authenticated())
                //安全上下文配置
                .securityContext(securityContextCustomizer -> securityContextCustomizer
                        //设置自定义securityContext仓库
                        .securityContextRepository(securityContextRepository)
                        //显示保存SecurityContext,官方推荐
                        .requireExplicitSave(true))
                //登出配置
                .logout(logoutCustomizer -> logoutCustomizer
                        //登出地址
                        .logoutUrl(LOGOUT_URL)
                        //登出成功处理器
                        .logoutSuccessHandler(loginResultHandler)
                )
                //注册自定义登录过滤器的配置器:自动注册自定义登录过滤器;
                //需要重写FilterOrderRegistration的构造方法FilterOrderRegistration(){},在构造方法中添加自定义过滤器的序号,否则注册不成功
                .apply(new RestfulLoginConfigurer<>(new RestfulUsernamePasswordAuthenticationFilter(LOGIN_ARG_USERNAME, LOGIN_ARG_PASSWORD, LOGIN_URL, LOGIN_HTTP_METHOD), LOGIN_URL, LOGIN_HTTP_METHOD))
                //设置登录地址:未设置时系统默认生成登录页面,登录地址/login
                .loginPage(LOGIN_URL)
                //设置登录成功之后的处理器
                .successHandler(loginResultHandler);

        //创建过滤器链对象
        return httpSecurity.build();
    }

}

八、其他类

package com.yu.demo.util;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.parser.Feature;

import java.lang.reflect.Type;
import java.util.Map;

/**
 * JSON工具类
 *
 * @author admin
 */
public class JsonUtil {
    private JsonUtil() {
        throw new AssertionError();
    }

    /**
     * 对象转json
     *
     * @param javaObject 对象或集合或者数组
     * @return json
     */
    public static String object2Json(Object javaObject) {
        return JSONObject.toJSONString(javaObject);
    }

    public static <K, V> Map<K, V> json2Map(String jsonString, Type type) {
        return JSON.parseObject(jsonString, type);
    }
}

package com.yu.demo.util;

import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext;

/**
 * Spring框架工具类
 */
public class SecurityUtil {

    private SecurityUtil() {
        throw new AssertionError();
    }

    public static boolean isAuthenticated(SecurityContext securityContext) {
        if (securityContext == null) {
            return false;
        }
        Authentication authentication = securityContext.getAuthentication();
        if (authentication == null) {
            return false;
        }
        return authentication.isAuthenticated();
    }

    public static boolean isNotAuthenticated(SecurityContext securityContext) {
        return !isAuthenticated(securityContext);
    }

}

package com.yu.demo.util;

import org.springframework.http.MediaType;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.Map;

/**
 * Spring框架工具类
 */
public class SpringUtil {

    private SpringUtil() {
        throw new AssertionError();
    }

    /**
     * 请求body参数转为map
     *
     * @param request 请求
     * @return 参数map
     * @throws IOException IO流异常
     */
    public static Map<String, String> rawBodyToMap(HttpServletRequest request) throws IOException {
        BufferedReader streamReader = new BufferedReader(new InputStreamReader(request.getInputStream(), StandardCharsets.UTF_8));
        StringBuilder responseStrBuilder = new StringBuilder();
        String inputStr;
        while ((inputStr = streamReader.readLine()) != null) {
            responseStrBuilder.append(inputStr);
        }
        return JsonUtil.json2Map(responseStrBuilder.toString(), Map.class);
    }


    public static void respJson(HttpServletResponse response, Map<String, Object> apiResp) throws IOException {
        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        response.setCharacterEncoding(StandardCharsets.UTF_8.name());
        response.getWriter().print(JsonUtil.object2Json(apiResp));
    }

}

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.18</version>
        <relativePath/>
    </parent>

    <groupId>com.yu</groupId>
    <artifactId>spring-boot-security2-demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>spring-boot-security2-demo</name>
    <description>Spring Boot集成Spring Security样例</description>

    <properties>
        <java.version>8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <!--fastjson-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.46</version>
        </dependency>
        <!--过期map-->
        <dependency>
            <groupId>net.jodah</groupId>
            <artifactId>expiringmap</artifactId>
            <version>0.5.11</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.0</version>
        </dependency>
    </dependencies>

</project>

九、案例源码获取

标签:web,Spring,Boot,springframework,token,import,org,Security,security
From: https://www.cnblogs.com/sanxiaolq/p/18531082

相关文章

  • 基于Java+SpringBoot心理测评心理测试系统功能实现四
    一、前言介绍:1.1项目摘要心理测评和心理测试系统在当代社会中扮演着越来越重要的角色。随着心理健康问题日益受到重视,心理测评和心理测试系统作为评估个体心理状态、诊断心理问题、制定心理治疗方案的工具,其需求和应用范围不断扩大。首先,现代社会节奏快速,竞争激烈,人们面临着来......
  • CentOS7-InitializeSecurity
    CentOS7-InitializeSecurityCentOS7-InitializeSecurity.sh#!/bin/bash##@Author:liyanjing,@E-mail:[email protected],@wechat:Sd-LiYanJing#@CreateTime:2022-10-1810:30,@LastModifiedTime:2022-12-0313:50#@Github:https://github.com/919927181/li......
  • SpringBoot小说阅读网s8w30--(程序+源码+数据库+调试部署+开发环境)
    本系统(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。系统程序文件列表开题报告内容一、研究背景与意义随着互联网技术的普及,网络文学逐渐成为一种重要的文化现象。然而,当前市场上的小说阅读网站大多存在功能单一、用户体验不佳等问......
  • SpringBoot小学生在线数学学习平台m0ncg(程序+源码+数据库+调试部署+开发环境)
    本系统(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。系统程序文件列表开题报告内容一、研究背景与意义随着互联网技术的普及,在线学习已成为教育领域的重要趋势。小学生作为基础教育阶段的重要群体,其数学学习效果直接影响到后续学业......
  • SpringBoot小区垃圾分类系统62nc6(程序+源码+数据库+调试部署+开发环境)
    本系统(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。系统程序文件列表开题报告内容一、研究背景随着城市化进程的推进,小区垃圾处理问题日益突出。传统的垃圾处理方式不仅效率低下,而且对环境造成了严重污染。垃圾分类作为一种有效的......
  • 基于springboot蛋糕订购小程序的设计与实现
    摘要相比于以前的传统手工管理方式,智能化的管理方式可以大幅降低商家的运营人员成本,实现了蛋糕订购的标准化、制度化、程序化的管理,有效地防止了蛋糕订购的随意管理,提高了信息的处理速度和精确度,能够及时、准确地查询和修正蛋糕信息、购物车、订单等信息。系统主要采用Un......
  • SpringCloud-Nacos注册中心
    3.Nacos注册中心国内公司一般都推崇阿里巴巴的技术,比如注册中心,SpringCloudAlibaba也推出了一个名为Nacos的注册中心。3.1.认识和安装NacosNacos是阿里巴巴的产品,现在是SpringCloud中的一个组件。相比Eureka功能更加丰富,在国内受欢迎程度较高。安装方式可以参考资料《N......
  • SpringBoot小小主持人网站7q3we(程序+源码+数据库+调试部署+开发环境)
    本系统(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。系统程序文件列表开题报告内容一、项目背景随着社会对儿童综合素质培养的重视,特别是在语言表达和公众演讲能力方面,小小主持人课程受到越来越多家长和孩子的青睐。为满足这一市场......
  • SpringBoot小区物业管理系统3248a--程序+源码+数据库+调试部署+开发环境
    本系统(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。系统程序文件列表开题报告内容一、研究背景与意义随着城市化进程的加速,小区物业管理面临着越来越复杂和多元的挑战。传统的人工管理方式不仅效率低下,还难以满足业主日益增长的多......
  • SpringBoot线上评分分享平台s7103(程序+源码+数据库+调试部署+开发环境)
    本系统(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。系统程序文件列表开题报告内容一、研究背景随着互联网技术的飞速发展,线上消费和在线评价已成为人们日常生活的重要组成部分。然而,现有的线上评分系统往往局限于特定领域,无法满足......