SpringSecurity入门(三)

12、密码加密

12.1、不指定具体加密方式,通过DelegatingPasswordEncoder,根据前缀自动选择

PasswordEncoder passwordEncoder =PasswordEncoderFactories.createDelegatingPasswordEncoder();

image.png

12.2、指定具体加密方式

// Create an encoder with strength 16
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(16);
String result = encoder.encode("myPassword");
assertTrue(encoder.matches("myPassword", result));// Create an encoder with all the defaults
Argon2PasswordEncoder encoder = new Argon2PasswordEncoder();
String result = encoder.encode("myPassword");
assertTrue(encoder.matches("myPassword", result));// Create an encoder with all the defaults
Pbkdf2PasswordEncoder encoder = new Pbkdf2PasswordEncoder();
String result = encoder.encode("myPassword");
assertTrue(encoder.matches("myPassword", result));// Create an encoder with all the defaults
SCryptPasswordEncoder encoder = new SCryptPasswordEncoder();
String result = encoder.encode("myPassword");
assertTrue(encoder.matches("myPassword", result));

13、密码自动更新

image.png
实现UserDetailsPasswordService接口即可
image.png

@Component("userDetailsImpl")
public class UserDetailsImpl implements UserDetailsService, UserDetailsPasswordService {@Autowiredprivate UserMapper userMapper;@Autowiredprivate RoleMapper roleMapper;@Overridepublic org.springframework.security.core.userdetails.UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {User user = userMapper.loadUserByUsername(username);if(user == null){throw new UsernameNotFoundException("用户名不存在");}List<Role> roles = roleMapper.getRoleByUserId(user.getUserId());user.setRoles(roles);return user;}@Overridepublic UserDetails updatePassword(UserDetails user, String newPassword) {int state = userMapper.updatePassword(newPassword, user.getUsername());if (state == 1) {((User) user).setPassword(newPassword);}return user;}
}

14、Remember-Me

14.1、传统web方式

14.1.1、login.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"xmlns:sec="http://www.thymeleaf.org/extras/spring-security">
<head><meta charset="UTF-8"><title>login</title>
</head>
<body>
<form th:action="@{/doLogin}" method="post"><p>用户名:<label><input name="uname" type="text"/></label></p><p>密码:<label><input name="pwd" type="password"/></label></p><p>记住我:<label><input name="remember-me" type="checkbox"/></label></p><p><input type="submit"></p>
</form></body>
</html>

14.1.2、开启rememberMeServices

  • 基于内存实现
package com.wanqi.config;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenBasedRememberMeServices;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.OrRequestMatcher;import java.util.UUID;@Configuration
@EnableWebSecurity
public class WebSecurityConfigurer {@Beanpublic PasswordEncoder bcryptPasswordEncoder() {return PasswordEncoderFactories.createDelegatingPasswordEncoder();}public UserDetailsService inMemoryUsers(PasswordEncoder encoder) {// The builder will ensure the passwords are encoded before saving in memoryUserDetails user = User.withUsername("user").password(encoder.encode("123")).roles("USER").build();UserDetails admin = User.withUsername("admin").password(encoder.encode("123")).roles("USER", "ADMIN").build();return new InMemoryUserDetailsManager(user, admin);}@Bean(name = "rememberMeServices")public PersistentTokenBasedRememberMeServices rememberMeServices(){return new PersistentTokenBasedRememberMeServices(UUID.randomUUID().toString(), inMemoryUsers(bcryptPasswordEncoder()), new InMemoryTokenRepositoryImpl());}@BeanSecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {return//开启权限验证httpSecurity.authorizeRequests()//permitAll直接放行,必须在anyRequest().authenticated()前面.mvcMatchers("/toLogin").permitAll()//anyRequest所有请求都需要认证.anyRequest().authenticated().and()//使用form表单验证.formLogin(login ->login//自定义登陆页面.loginPage("/toLogin")//自定义登陆页面后必须指定处理登陆请求的url.loginProcessingUrl("/doLogin")// 自定义接收用户名的参数名为uname.usernameParameter("uname")//自定义接收密码的参数名为pwd.passwordParameter("pwd")// 认证成功后跳转的页面(转发),必须使用POST请求//.successForwardUrl("/test")// 证成功后跳转的页面(重定向),必须使用GET请求//.defaultSuccessUrl("/test")//不会每次都跳转定义的页面,默认会记录认证拦截的请求,如果是拦截的受限资源会优先跳转到之前被拦截的请求。需要每次都跳转使defaultSuccessUrl("/test",true).defaultSuccessUrl("/toLogout",true)//前后端分离时代自定义认证成功处理
//                                        .successHandler(new MyAuthenticationSuccessHandler())//前后端分离时代自定义认证失败处理
//                                        .failureHandler(new MyAuthenticationFailureHandler()).failureUrl("/404"))//注销.logout(logout -> {logout//指定默认注销url,默认请求方式GET
//                                    .logoutUrl("/logout").logoutRequestMatcher(//自定义注销urlnew OrRequestMatcher(new AntPathRequestMatcher("/aa", "GET"),new AntPathRequestMatcher("/bb", "POST")))//注销成功后跳转页面.logoutSuccessUrl("/toLogin")//前后端分离时代自定义注销登录处理器
//                                    .logoutSuccessHandler(new MyLogoutSuccessHandler())//销毁session,默认为true.invalidateHttpSession(true)//清除认证信息,默认为true.clearAuthentication(true);})//指定UserDetailsService来切换认证信息不同的存储方式(数据源).userDetailsService(inMemoryUsers(bcryptPasswordEncoder()))//开启rememberMe.rememberMe()//自定义rememberMe参数名
//                        .rememberMeParameter();.rememberMeServices(rememberMeServices()).and()//禁止csrf跨站请求保护.csrf().disable().build();}
}
  • 持久化,基于mysql实现
    @Beanpublic PersistentTokenRepository jdbcTokenRepository(){JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();jdbcTokenRepository.setDataSource(dataSource);//自动创建表结构,首次启动设置为true
//        jdbcTokenRepository.setCreateTableOnStartup(true);return jdbcTokenRepository;}@BeanSecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {return//开启权限验证httpSecurity.authorizeRequests()//permitAll直接放行,必须在anyRequest().authenticated()前面.mvcMatchers("/toLogin").permitAll().mvcMatchers("/404").permitAll()//anyRequest所有请求都需要认证.anyRequest().authenticated().and()//使用form表单验证.formLogin(login ->login//自定义登陆页面.loginPage("/toLogin")//自定义登陆页面后必须指定处理登陆请求的url.loginProcessingUrl("/doLogin")// 自定义接收用户名的参数名为uname.usernameParameter("uname")//自定义接收密码的参数名为pwd.passwordParameter("pwd")// 认证成功后跳转的页面(转发),必须使用POST请求//.successForwardUrl("/test")// 证成功后跳转的页面(重定向),必须使用GET请求//.defaultSuccessUrl("/test")//不会每次都跳转定义的页面,默认会记录认证拦截的请求,如果是拦截的受限资源会优先跳转到之前被拦截的请求。需要每次都跳转使defaultSuccessUrl("/test",true).defaultSuccessUrl("/toLogout",true)//前后端分离时代自定义认证成功处理
//                                        .successHandler(new MyAuthenticationSuccessHandler())//前后端分离时代自定义认证失败处理
//                                        .failureHandler(new MyAuthenticationFailureHandler()).failureUrl("/404"))//注销.logout(logout -> {logout//指定默认注销url,默认请求方式GET
//                                    .logoutUrl("/logout").logoutRequestMatcher(//自定义注销urlnew OrRequestMatcher(new AntPathRequestMatcher("/aa", "GET"),new AntPathRequestMatcher("/bb", "POST")))//注销成功后跳转页面.logoutSuccessUrl("/toLogin")//前后端分离时代自定义注销登录处理器
//                                    .logoutSuccessHandler(new MyLogoutSuccessHandler())//销毁session,默认为true.invalidateHttpSession(true)//清除认证信息,默认为true.clearAuthentication(true);})//指定UserDetailsService来切换认证信息不同的存储方式(数据源).userDetailsService(inMemoryUsers(bcryptPasswordEncoder()))//开启rememberMe.rememberMe().tokenRepository(jdbcTokenRepository()).and()//禁止csrf跨站请求保护.csrf().disable().build();}
}

14.2、前后端分离

14.2.1、自定义RememberMeServices实现类

package com.wanqi.service;import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.rememberme.AbstractRememberMeServices;
import org.springframework.security.web.authentication.rememberme.PersistentTokenBasedRememberMeServices;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;import javax.servlet.http.HttpServletRequest;/*** @Description 自定义RememberMeServices实现类* @Version 1.0.0* @Date 2022/9/5* @Author wandaren*/public class RememberMeServices extends PersistentTokenBasedRememberMeServices {public RememberMeServices(String key, UserDetailsService userDetailsService, PersistentTokenRepository tokenRepository) {super(key, userDetailsService, tokenRepository);}@Overrideprotected boolean rememberMeRequested(HttpServletRequest request, String parameter) {String paramValue = request.getAttribute(AbstractRememberMeServices.DEFAULT_PARAMETER).toString();return paramValue.equalsIgnoreCase("true") || paramValue.equalsIgnoreCase("on")|| paramValue.equalsIgnoreCase("yes") || paramValue.equals("1");}
}

14.2.2、自定义认证方式

package com.wanqi.security.filter;import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.AuthenticationManager;
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.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.rememberme.AbstractRememberMeServices;
import org.springframework.util.ObjectUtils;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Map;/*** @Description 处理Json方式的登录请求* @Version 1.0.0* @Date 2022/8/21* @Author wandaren*/
public class JsonLoginFilter extends UsernamePasswordAuthenticationFilter {public JsonLoginFilter(AuthenticationManager authenticationManager) {super(authenticationManager);}@Overridepublic Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {if (!request.getMethod().equals("POST")) {throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());}if (request.getContentType().equalsIgnoreCase(MediaType.APPLICATION_JSON_VALUE)) {try {Map<String, String> map = new ObjectMapper().readValue(request.getInputStream(), Map.class);String username = map.get(getUsernameParameter());username = (username != null) ? username.trim() : "";String password = map.get(getPasswordParameter());password = (password != null) ? password : "";String rememberMe = map.get(AbstractRememberMeServices.DEFAULT_PARAMETER);if (!ObjectUtils.isEmpty(rememberMe)) {request.setAttribute(AbstractRememberMeServices.DEFAULT_PARAMETER, rememberMe);}UsernamePasswordAuthenticationToken authRequest = UsernamePasswordAuthenticationToken.unauthenticated(username,password);System.out.println(username);System.out.println(password);setDetails(request, authRequest);return getAuthenticationManager().authenticate(authRequest);} catch (IOException e) {e.printStackTrace();}}throw new AuthenticationServiceException("Authentication ContentType not supported: " + request.getContentType());}
}

14.2.3、配置

filter.setRememberMeServices(rememberMeServices());.rememberMe()
.rememberMeServices(rememberMeServices())
.tokenRepository(jdbcTokenRepository())
package com.wanqi.config;import com.fasterxml.jackson.databind.ObjectMapper;
import com.wanqi.security.filter.JsonLoginFilter;
import com.wanqi.service.RememberMeServices;
import com.wanqi.service.impl.UserDetailsImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.Authentication;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenBasedRememberMeServices;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.security.web.util.matcher.OrRequestMatcher;import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.sql.DataSource;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;/*** @Description TODO* @Version 1.0.0* @Date 2022/9/5* @Author wandaren*/
@Configuration
@EnableWebSecurity
public class SecurityConfig {@AutowiredDataSource dataSource;UserDetailsImpl userDetailsImpl;@Autowiredpublic void setUserDetailsImpl(UserDetailsImpl userDetailsImpl) {this.userDetailsImpl = userDetailsImpl;}@Beanpublic PasswordEncoder bcryptPasswordEncoder() {return PasswordEncoderFactories.createDelegatingPasswordEncoder();}@Autowiredpublic AuthenticationConfiguration authenticationConfiguration;/*** 获取AuthenticationManager(认证管理器),登录时认证使用** @return* @throws Exception*/@Beanpublic AuthenticationManager authenticationManager() throws Exception {return authenticationConfiguration.getAuthenticationManager();}@Beanpublic JsonLoginFilter jsonLoginFilter() throws Exception {JsonLoginFilter filter = new JsonLoginFilter(authenticationManager());//指定认证urlfilter.setFilterProcessesUrl("/doLogin");//指定接收json,用户名keyfilter.setUsernameParameter("uname");//指定接收json,密码keyfilter.setPasswordParameter("pwd");filter.setAuthenticationManager(authenticationManager());filter.setRememberMeServices(rememberMeServices());//前后端分离时代自定义认证成功处理filter.setAuthenticationSuccessHandler(new AuthenticationSuccessHandler() {@Overridepublic void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {Map<String, Object> map = new HashMap<>();map.put("msg", "登陆成功");map.put("code", HttpStatus.OK.value());map.put("authentication", authentication);String s = new ObjectMapper().writeValueAsString(map);response.setContentType("application/json;charset=UTF-8");response.getWriter().write(s);}});//前后端分离时代自定义认证失败处理filter.setAuthenticationFailureHandler((request, response, exception) -> {Map<String, Object> map = new HashMap<>();map.put("msg", exception.getMessage());map.put("code", HttpStatus.INTERNAL_SERVER_ERROR.value());String s = new ObjectMapper().writeValueAsString(map);response.setContentType("application/json;charset=UTF-8");response.getWriter().write(s);});return filter;}@Beanpublic PersistentTokenRepository jdbcTokenRepository(){JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();jdbcTokenRepository.setDataSource(dataSource);//自动创建表结构,首次启动设置为true
//        jdbcTokenRepository.setCreateTableOnStartup(true);return jdbcTokenRepository;}@Beanpublic RememberMeServices rememberMeServices(){return new RememberMeServices(UUID.randomUUID().toString(), userDetailsImpl, jdbcTokenRepository());}@Beanpublic SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {return httpSecurity.authorizeRequests().anyRequest().authenticated().and().formLogin().and().addFilterAt(jsonLoginFilter(), UsernamePasswordAuthenticationFilter.class).logout(logout -> {logout.logoutRequestMatcher(//自定义注销urlnew OrRequestMatcher(new AntPathRequestMatcher("/aa", "GET"),new AntPathRequestMatcher("/bb", "POST")))//前后端分离时代自定义注销登录处理器.logoutSuccessHandler(new LogoutSuccessHandler() {@Overridepublic void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {Map<String, Object> map = new HashMap<>();map.put("msg", "注销成功");map.put("code", HttpStatus.OK.value());map.put("authentication", authentication);String s = new ObjectMapper().writeValueAsString(map);response.setContentType("application/json;charset=UTF-8");response.getWriter().write(s);}})//销毁session,默认为true.invalidateHttpSession(true)//清除认证信息,默认为true.clearAuthentication(true);})//指定UserDetailsService来切换认证信息不同的存储方式(数据源).userDetailsService(userDetailsImpl).exceptionHandling().authenticationEntryPoint((request, response, authException) -> {
//                            response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);response.setHeader("content-type", "application/json;charset=UTF-8");response.setStatus(HttpStatus.UNAUTHORIZED.value());response.getWriter().write("请先认证后重试!");}).and().rememberMe().rememberMeServices(rememberMeServices()).tokenRepository(jdbcTokenRepository()).and()//禁止csrf跨站请求保护.csrf().disable().build();}}

15、会话管理

image.png

15.1、配置

package com.wanqi.config;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.session.HttpSessionEventPublisher;/*** @Description TODO* @Version 1.0.0* @Date 2022/9/5* @Author wandaren*/
@Configuration
@EnableWebSecurity
public class SecurityConfig {@BeanSecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception{httpSecurity.authorizeRequests().anyRequest().authenticated().and().formLogin().defaultSuccessUrl("/hello",true).and().csrf().disable().sessionManagement(session -> session//最多一个会话.maximumSessions(1)//true:超过会话上限不再容许登录
//                        .maxSessionsPreventsLogin(true)// 会话失效(用户被挤下线后)跳转地址// .expiredUrl("/login")//  前后端分离处理方式.expiredSessionStrategy(new SessionInformationExpiredStrategy() {@Overridepublic void onExpiredSessionDetected(SessionInformationExpiredEvent event) throws IOException, ServletException {event.getResponse().setHeader("content-type", "application/json;charset=UTF-8");event.getResponse().setStatus(HttpStatus.UNAUTHORIZED.value());event.getResponse().getWriter().write("会话超时!");}}));return httpSecurity.build();}@Beanpublic HttpSessionEventPublisher httpSessionEventPublisher() {return new HttpSessionEventPublisher();}
}

15.2、共享会话

15.2.1、引入依赖

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><dependency><groupId>org.springframework.session</groupId><artifactId>spring-session-data-redis</artifactId></dependency>

15.2.2、编写配置

  • application.yml
spring:redis:host: 172.16.156.139port: 6379password: qifengdatabase: 0main:allow-circular-references: true
  • RedisSessionConfig
package com.wanqi.config;import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.security.web.session.HttpSessionEventPublisher;/*** @Description TODO* @Version 1.0.0* @Date 2022/9/5* @Author wandaren*/
@Configuration
@EnableRedisHttpSession
public class RedisSessionConfig {@Value("${spring.redis.host}")private String host;@Value("${spring.redis.port}")private int port;@Value("${spring.redis.password}")private String password;@Value("${spring.redis.database}")private int database;@BeanRedisStandaloneConfiguration redisStandaloneConfiguration() {RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();redisStandaloneConfiguration.setHostName(host);redisStandaloneConfiguration.setPort(port);redisStandaloneConfiguration.setPassword(password);redisStandaloneConfiguration.setDatabase(database);return redisStandaloneConfiguration;}@Beanpublic LettuceConnectionFactory redisConnectionFactory() {return new LettuceConnectionFactory(redisStandaloneConfiguration());}@Beanpublic HttpSessionEventPublisher httpSessionEventPublisher() {return new HttpSessionEventPublisher();}
}
  • SecurityConfig
package com.wanqi.config;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.session.HttpSessionEventPublisher;
import org.springframework.security.web.session.SessionInformationExpiredEvent;
import org.springframework.security.web.session.SessionInformationExpiredStrategy;
import org.springframework.session.FindByIndexNameSessionRepository;
import org.springframework.session.Session;
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
import org.springframework.session.security.SpringSessionBackedSessionRegistry;import javax.servlet.ServletException;
import java.io.IOException;/*** @Description TODO* @Version 1.0.0* @Date 2022/9/5* @Author wandaren*/
@Configuration
@EnableWebSecurity
public class SecurityConfig<S extends Session> {@Autowiredprivate FindByIndexNameSessionRepository<S> sessionRepository;@Beanpublic PasswordEncoder bcryptPasswordEncoder() {return PasswordEncoderFactories.createDelegatingPasswordEncoder();}@Beanpublic UserDetailsService inMemoryUsers(PasswordEncoder encoder) {// The builder will ensure the passwords are encoded before saving in memoryUserDetails user = User.withUsername("user").password(encoder.encode("123")).roles("USER").build();UserDetails admin = User.withUsername("admin").password(encoder.encode("123")).roles("USER", "ADMIN").build();return new InMemoryUserDetailsManager(user, admin);}@BeanSecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception{httpSecurity.authorizeRequests().anyRequest().authenticated().and().formLogin().defaultSuccessUrl("/hello",true).and().csrf().disable().sessionManagement(session -> session//最多一个会话.maximumSessions(1)//true超过会话上限不再容许登录.maxSessionsPreventsLogin(true)
//                        被踢下线后跳转地址
//                        .expiredUrl("/login").expiredSessionStrategy(new SessionInformationExpiredStrategy() {@Overridepublic void onExpiredSessionDetected(SessionInformationExpiredEvent event) throws IOException, ServletException {event.getResponse().setHeader("content-type", "application/json;charset=UTF-8");event.getResponse().setStatus(HttpStatus.UNAUTHORIZED.value());event.getResponse().getWriter().write("会话超时!");}}).sessionRegistry(sessionRegistry())).userDetailsService(inMemoryUsers(bcryptPasswordEncoder()));return httpSecurity.build();}@Beanpublic SpringSessionBackedSessionRegistry<S> sessionRegistry() {return new SpringSessionBackedSessionRegistry<>(this.sessionRepository);}}

16、CSRF防御

16.1、传统web开发

  • 登录页面加上_csrf
 <input type="hidden" name="_csrf" th:value="${_csrf.getToken()}">
  • SecurityConfig配置修改
httpSecurity.csrf()

16.2、前后端分离开发

16.2.1、登陆页面不需要令牌

httpSecurity.csrf()//SpringSecurity处理登陆的默认方法不加令牌.ignoringAntMatchers("/doLogin")//将令牌保存到cookie,容许前端获取.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
  • 登陆后返回cookie中包含XSRF-TOKEN

image.png

  • 后续请求在请求头增加X-XSRF-TOKEN,值为登陆cookie中的XSRF-TOKEN值

image.png

17、跨越处理

17.1、spring跨越处理

17.1.1、使用@CrossOrigin,可以作用到类上也可以作用到具体方法上

@RestController
public class HelloController {@RequestMapping("/hello")@CrossOriginpublic String hello(){return "hello";}
}

17.1.2、实现WebMvcConfigurer接口重写addCorsMappings方法

package com.wanqi.config;import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;/*** @Description TODO* @Version 1.0.0* @Date 2022/9/6* @Author wandaren*/
@Configuration
public class WebConfig implements WebMvcConfigurer {@Overridepublic void addCorsMappings(CorsRegistry registry) {//对哪些请求进行跨域处理registry.addMapping("/**").allowCredentials(false).allowedHeaders("*").allowedMethods("*").allowedOrigins("*").exposedHeaders("").maxAge(3600);}
}

17.1.3、使用CorsFilter

    @BeanFilterRegistrationBean<CorsFilter> corsFilter() {FilterRegistrationBean<CorsFilter> registrationBean = new FilterRegistrationBean<>();CorsConfiguration corsConfiguration = new CorsConfiguration();corsConfiguration.setAllowedOrigins(Collections.singletonList("*"));corsConfiguration.setAllowedHeaders(Collections.singletonList("*"));corsConfiguration.setAllowedMethods(Collections.singletonList("*"));corsConfiguration.setMaxAge(3600L);UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();source.registerCorsConfiguration("/**", corsConfiguration);registrationBean.setFilter(new CorsFilter(source));//指定Filter执行顺序registrationBean.setOrder(-1);return registrationBean;}

17.2、SpringSecurity跨域处理

17.2.1、 Security配置

@Bean
public CorsConfigurationSource configurationSource() {CorsConfiguration corsConfiguration = new CorsConfiguration();corsConfiguration.setAllowedOrigins(Collections.singletonList("*"));corsConfiguration.setAllowedHeaders(Collections.singletonList("*"));corsConfiguration.setAllowedMethods(Collections.singletonList("*"));corsConfiguration.setMaxAge(3600L);UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();source.registerCorsConfiguration("/**", corsConfiguration);return source;
}httpSecurity.cors().configurationSource(configurationSource())

相关文章

SpringSecurity入门(一)

SpringSecurity入门(二)

SpringSecurity入门(四)

未完待续

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/25805.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【iOS】UI学习——登陆界面案例、照片墙案例

文章目录 登陆界面案例照片墙案例 登陆界面案例 这里通过一个登陆界面来复习一下前面学习的内容。 先在接口部分定义两个UILabel、两个UITextField、两个UIButton按键&#xff1a; #import <UIKit/UIKit.h>interface ViewController : UIViewController {UILabel* _lb…

2024050501-重学 Java 设计模式《实战命令模式》

重学 Java 设计模式&#xff1a;实战命令模式「模拟高档餐厅八大菜系&#xff0c;小二点单厨师烹饪场景」 一、前言 持之以恒的重要性 初学编程往往都很懵&#xff0c;几乎在学习的过程中会遇到各种各样的问题&#xff0c;哪怕别人那运行好好的代码&#xff0c;但你照着写完…

Python数据分析与机器学习在电子商务推荐系统中的应用

文章目录 &#x1f4d1;引言一、推荐系统的类型二、数据收集与预处理2.1 数据收集2.2 数据预处理 三、基于内容的推荐3.1 特征提取3.2 计算相似度3.3 推荐物品 四、协同过滤推荐4.1 基于用户的协同过滤4.2 基于物品的协同过滤 五、混合推荐与评估推荐系统5.1 结合推荐结果5.2 评…

Qwen2本地部署的实战教程

大家好,我是herosunly。985院校硕士毕业,现担任算法研究员一职,热衷于机器学习算法研究与应用。曾获得阿里云天池比赛第一名,CCF比赛第二名,科大讯飞比赛第三名。拥有多项发明专利。对机器学习和深度学习拥有自己独到的见解。曾经辅导过若干个非计算机专业的学生进入到算法…

网络安全技术实验一 信息收集和漏洞扫描

一、实验目的和要求 了解信息搜集和漏洞扫描的一般步骤&#xff0c;利用Nmap等工具进行信息搜集并进行综合分析&#xff1b;掌握TCP全连接扫描、TCP SYN扫描的原理,利用Scapy编写网络应用程序&#xff0c;开发端口扫描功能模块&#xff1b;使用漏洞扫描工具发现漏洞并进行渗透测…

8款高效电脑维护与多媒体工具合集!

AI视频生成&#xff1a;小说文案智能分镜智能识别角色和场景批量Ai绘图自动配音添加音乐一键合成视频https://h5.cxyhub.com/?invitationhmeEo7 1. 系统安装利器——WinNTSetup 系统安装利器&#xff0c;目前最好用的系统安装器&#xff0c;Windows系统安装部署工具。支持所…

跟我学,数据结构和组原真不难

我个人认为408中计算机组成原理和数据结构最难 难度排行是计算机组成原理>数据结构>操作系统>计算机网络。 计算机组成原理比较难的原因是&#xff0c;他涉及的硬件的知识比较多&#xff0c;这对于大家来说难度就很高了&#xff0c;特别是对于跨考的同学来说&#x…

ABB机械人模型下载

可以下载不同格式的 https://new.abb.com/products/robotics/zh/robots/articulated-robots/irb-6700 step的打开各部件是分开的&#xff0c;没有装配在一起&#xff0c;打开看单个零件时&#xff0c;我们会发现其各零件是有装配的定位关系的。 新建一个装配环境&#xff0c;点…

【qt】MDI多文档界面开发

MDI多文档界面开发 一.应用场景二.界面设计三.界面类设计四.实现功能1.新建文档2.打开文件3.关闭所有4.编辑功能5.MDI页模式6.瀑布展开模式7.平铺模式 五.总结 一.应用场景 类似于vs的界面功能,工具栏的功能可以对每个文档使用! 二.界面设计 老规矩,边做项目边学! 目标图: 需…

【JMeter接口测试工具】第二节.JMeter项目实战(上)【实战篇】

文章目录 前言项目实战零、接口测试流程一、测试数据准备二、接口功能测试三、掌握测试用例编写四、自动化脚本架构搭建总结 前言 零、接口测试流程 1、制定测试计划,分配任务 2、从 API 文档中提取接口清单&#xff1a;对 API 文档简化,提高测试效率,接口清单就是对 API 文档…

【研发日记】Matlab/Simulink软件优化(三)——利用NaNFlag为数据处理算法降阶

文章目录 前言 背景介绍 初始算法 优化算法 分析和应用 总结 前言 见《【研发日记】Matlab/Simulink软件优化(一)——动态内存负荷压缩》 见《【研发日记】Matlab/Simulink软件优化(二)——通信负载柔性均衡算法》 背景介绍 在一个嵌入式软件开发项目中&#xff0c;需要开…

OpenAI与核聚变公司寻求合作,白宫拨款1.8亿美元用于核聚变商业化研究

在当下&#xff0c;由 AI 引发的新一轮能源危机已经不再是一个小概率的「黑天鹅」事件&#xff0c;而是一头正在向我们猛冲而来的「灰犀牛」。 Helion Energy&#xff0c;是一家总部位于美国华盛顿州埃弗雷特的能源创业公司。 3.5研究测试&#xff1a;hujiaoai.cn 4研究测试&am…

Mysql的底层实现逻辑

Mysql5.x和Mysql8性能的差异 整体性能有所提高&#xff0c; 在非高并发场景下&#xff0c;他们2这使用区别不大&#xff0c;性能没有明显的区别。 只有高并发时&#xff0c;mysql8才体现他的优势。 2. Mysql数据存储结构Innodb逻辑结构 数据选用B树结构存储数据&#xff0…

如何画系统架构图学习

原文链接:https://learn.lianglianglee.com/%E4%B8%93%E6%A0%8F/%E4%BB%8E%200%20%E5%BC%80%E5%A7%8B%E5%AD%A6%E6%9E%B6%E6%9E%84/51%20%E5%A6%82%E4%BD%95%E7%94%BB%E5%87%BA%E4%BC%98%E7%A7%80%E7%9A%84%E8%BD%AF%E4%BB%B6%E7%B3%BB%E7%BB%9F%E6%9E%B6%E6%9E%84%E5%9B%BE%EF…

C++ 11 【可变参数模板】【lambda】

&#x1f493;博主CSDN主页:麻辣韭菜&#x1f493;   ⏩专栏分类&#xff1a;C修炼之路⏪   &#x1f69a;代码仓库:C高阶&#x1f69a;   &#x1f339;关注我&#x1faf5;带你学习更多C知识   &#x1f51d;&#x1f51d; 目录 前言 一、新的类功能 1.1默认成员函数—…

【数据分析基础】实验三 文件操作、数组与矩阵运算

一&#xff0e;实验目的 掌握上下文管理语句with的使用方法。掌握文本文件的操作方法。了解os、os.path模块的使用。掌握扩展库Python-docx、openpyxl的安装与操作word、Excel文件内容的方法。熟练掌握numpy数组相关运算和简单应用。熟练使用numpy创建矩阵&#xff0c;熟悉常用…

新技术前沿-2024-构建个人知识库和小语言模型

OllamaWebUIAnythingLLM&#xff0c;构建安全可靠的个人/企业知识库 1 技术路线一 1.1 搭建本地大模型Ollama 1.2 搭建用户界面open WebUI 使用Docker Desktop Open-webui。它可以快速基于Ollama构筑本地UI。 如果没有科学上网&#xff0c;很可能会拉不动&#xff0c;可以试…

linux网络服务“PXE网络批量装机和Kickstart全自动化安装”

PXE网络批量装机 pxe自动装机&#xff1a; 服务端和客户端 pxe c/s 模式&#xff1a;允许客户端通过网络从远程服务器&#xff08;服务端&#xff09;下载引导镜像&#xff0c;加载安装文件&#xff0c;实现自动化安装操作系统。 无人值守 :安装选项不需要人为干预&#xf…

FlexJavaFramwork

FlexJavaFramwork架构

【高校科研前沿】广西大学博士生冯德东为一作在Habitat Int发文:区域乡村性与贫困治理变化的时空格局及相关效应——以滇桂黔石漠化地区为例

1.文章简介 论文名称&#xff1a;Spatio-temporal patterns and correlation effects of regional rurality and poverty governance change: A case study of the rocky desertification area of Yunnan-Guangxi-Guizhou, China&#xff08;区域乡村性与贫困治理变化的时空格…