二、自定义用户名密码认证过滤器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>

九、案例源码获取

七、Spring Boot集成Spring Security之前后分离认证最佳实现的更多相关文章

  1. Spring Boot集成Spring Data Reids和Spring Session实现Session共享

    首先,需要先集成Redis的支持,参考:http://www.cnblogs.com/EasonJim/p/7805665.html Spring Boot集成Spring Data Redis+Sp ...

  2. SpringBoot系列:Spring Boot集成Spring Cache,使用EhCache

    前面的章节,讲解了Spring Boot集成Spring Cache,Spring Cache已经完成了多种Cache的实现,包括EhCache.RedisCache.ConcurrentMapCac ...

  3. SpringBoot系列:Spring Boot集成Spring Cache,使用RedisCache

    前面的章节,讲解了Spring Boot集成Spring Cache,Spring Cache已经完成了多种Cache的实现,包括EhCache.RedisCache.ConcurrentMapCac ...

  4. Spring Boot 集成 Spring Security 实现权限认证模块

    作者:王帅@CodeSheep   写在前面 关于 Spring Security Web系统的认证和权限模块也算是一个系统的基础设施了,几乎任何的互联网服务都会涉及到这方面的要求.在Java EE领 ...

  5. Spring boot 集成Spring Security

    依赖jar <dependency> <groupId>org.springframework.cloud</groupId> <artifactId> ...

  6. Spring Boot 集成spring security4

    项目GitHub地址 : https://github.com/FrameReserve/TrainingBoot Spring Boot (三)集成spring security,标记地址: htt ...

  7. Spring boot集成spring session实现session共享

    最近使用spring boot开发一个系统,nginx做负载均衡分发请求到多个tomcat,此时访问页面会把请求分发到不同的服务器,session是存在服务器端,如果首次访问被分发到A服务器,那么se ...

  8. Spring Boot 集成 Spring Security

    1.添加依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId> ...

  9. Spring Boot 集成 Spring Security 入门案例教程

    前言 本文作为入门级的DEMO,完全按照官网实例演示: 项目目录结构 Maven 依赖 <parent> <groupId>org.springframework.boot&l ...

  10. Spring Boot 集成 Spring Security 使用自定义的安全数据源

    编写一个类自定义实现 UserDetailsService 接口 @Service("customUserDetailService") public class CustomUs ...

随机推荐

  1. Keil uVision5软件破解方法

    1.正常下载软件 2.右键"管理员方式运行"软件 3.如下图"File"->"License..." 4.复制CID,管理员方式打开破 ...

  2. ucos cpu_a.asm 注释

    EXPORT CPU_SR_Save import:翻译为进口或引入,表明要调用的函数为外部文件定义 export:翻译为出口或输出,表明该符号可以被外部模块使用,类似于C中的extern功能. ;* ...

  3. 电子行业MES系统流程图梳理

  4. 什么?!90%的ThreadLocal都在滥用或错用!

    最近在看一个系统代码时,发现系统里面在使用到了 ThreadLocal,乍一看,好像很高级的样子.我再仔细一看,这个场景并不会存在线程安全问题,完全只是在一个方法中传参使用的啊!(震惊) 难道是我水平 ...

  5. 痞子衡嵌入式:在IAR开发环境下将尽可能多的代码重定向到RAM中执行的方法

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家分享的是在IAR开发环境下将尽可能多的代码重定向到RAM中执行的方法. 最近和同事在讨论一个客户案例,客户 APP 工程是基于 IAR 开发环境 ...

  6. 【Python】之pip安装第三方库失败

    一直报错:Could not fetch URL https://pypi.org/simple/pygame/: There was a problem confirming the ssl cer ...

  7. CentOS 7 yum无法使用解决方法Could not retrieve mirrorlist http://mirrorlist.centos.org/?release=7&arch=

    在centos7中使用yum命令时候报错: Loading mirror speeds from cached hostfile Could not retrieve mirrorlist http: ...

  8. 【YashanDB知识库】ycm托管数据库时报错OM host ip:127.0.0.1 is not support join to YCM

    问题现象 托管数据库时检查报错OM的IP是127.0.0.1,不支持托管到YCM OM 问题的风险及影响 导致数据库无法托管监控 问题影响的版本 问题发生原因 安装数据库时修改了OM的监听ip为127 ...

  9. Word字体与像素的对应关系

    英文字体的1磅(pt),相当于1/72 英寸(inch),约等于1/2.8mm.12PT的字打印出来约为4.2mm.网页中12px的字才相当于12像素. 虽然 四号=(14/72)*96=18.6px ...

  10. JavaScript – 基本语法

    参考 阮一峰 – 基本语法 Switch switch 经常用来取代 else if, 因为可读性比价高, 而且通常性能也比较好. standard 长这样 const orderStatus = ' ...