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.rhkb.cn/news/346660.html

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

相关文章

【服务实现读写分离】

文章目录 什么是读写分离基于Spring实现实现读写分离项目中常用的数据源切换依赖包 什么是读写分离 服务读写分离&#xff08;Service Read-Write Splitting&#xff09;是一种常见的数据库架构设计模式&#xff0c;旨在提高系统的性能和可扩展性。通过将读操作和写操作分离到…

javaspringbootmysql小程序的竞赛管理系统71209-计算机毕业设计项目选题推荐(附源码)

摘 要 随着社会的发展,社会的方方面面都在利用信息化时代的优势。互联网的优势和普及使得各种系统的开发成为必需。 本文以实际运用为开发背景, 运用软件工程原理和开发方法,它主要是采用java语言技术和mysql数库来完成对系统的设计。整个开发过程首先对竞赛管理系统进行需求分…

OceanBase 4.3 特性解析:列存技术

在涉及大规模数据的复杂分析或即时查询时&#xff0c;列式存储是支撑业务负载的关键技术之一。相较于传统的行式存储&#xff0c;列式存储采用了不同的数据文件组织方式&#xff0c;它将表中的数据以列为单位进行物理排列。这种存储模式允许在分析过程中&#xff0c;查询计算仅…

mmdetection使用未定义backbone训练

首先找到你需要用到的 backbone&#xff0c;一般有名的backbone 都会在github有相应的代码开源和预训练权重提供 本文以mobilenetv3 fastercnn 作为举例&#xff0c;在mmdetection中并未提供 mobilenetv3&#xff0c;提供的仅有 mobilenetv2&#xff1b; 在github上找到 mobil…

高性能MySQL(第3版)电子书笔记

Mysql官方文档&#xff1a;https://dev.mysql.com/doc/refman/5.7/en/ 高性能MySQL&#xff08;第3版&#xff09;&#xff1a;百度网盘&#xff0c;基于Mysql5.1和Mysql5.5 本机版本 mysql> select version(); ------------ | version() | ------------ | 5.7.32-log |…

Linux 网络设置

Linux 网络设置 查看及测试网络查看网络配置测试网络连接 设置网络地址参数使用网络配置命令修改网络配置文件 查看及测试网络 查看及测试网络配置是管理 Linux 网络服务的第一步,本节将学习 Linux 操作系统中的网络查看及测试命令。其中讲解的大多数命令以普通用户权限就可以…

【ppyoloe+】19届智能车完全模型组非官方基线

基于十九届智能车百度完全模型组线上赛baseline修改 调整参数最高能到0.989吧 一、环境准备 1.安装PaddleDetection In [1] # 解压PaddleDetection压缩包 %cd /home/aistudio/data/data267567 !unzip -q PaddleDetection-release-2.6.zip -d /home/aistudio /home/aistud…

初识C++ · 反向迭代器简介

目录 前言 反向迭代器的实现 前言 继模拟实现了list和vector之后&#xff0c;我们对迭代器的印象也是加深了许多&#xff0c;但是我们实现的都是正向迭代器&#xff0c;还没有实现反向迭代器&#xff0c;那么为什么迟迟不实现呢&#xff1f;因为难吗&#xff1f;实际上还好。…

stm32MP135裸机编程:修改官方GPIO例程在DDR中点亮第一颗LED灯

0 参考资料 轻松使用STM32MP13x - 如MCU般在cortex A核上裸跑应用程序.pdf 正点原子stm32mp135开发板&原理图 STM32Cube_FW_MP13_V1.1.0 STM32CubeIDE v1.151 需要修改那些地方 1.1 修改LED引脚 本例使用开发板的PI3引脚链接的LED作为我们点亮的第一颗LED灯&#xff0c;…

AC/DC电源模块的原理、特点以及其在实际应用中的重要性

BOSHIDA AC/DC电源模块的原理、特点以及其在实际应用中的重要性 AC/DC电源模块是一种用于将交流电转换为直流电的设备&#xff0c;广泛应用于各种电子设备中。这种电源模块可以有效地将电力从电网中提取出来&#xff0c;并将其转换为稳定的直流电源&#xff0c;供给各种不同功…

容器(Docker)安装

centos安装Docker sudo yum remove docker* sudo yum install -y yum-utils#配置docker的yum地址 sudo yum-config-manager \ --add-repo \ http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo#安装指定版本 - 可以根据实际安装版本 sudo yum install -y docke…

12个精选Prompt框架,快速提升你写Prompt的能力,内附实例(上篇)

前言 想要熟练驾驭大模型&#xff0c;除了掌握Prompt的原则和技巧外&#xff0c;我们还可以参考一些成熟的Prompt框架&#xff0c;这样能快速提升我们写Prompt的能力&#xff0c;我从网上搜集到了12个精选Prompt框架&#xff0c;并为每一个框架附上一个实际的例子&#xff0c;…

何为屎山代码?

在编程界&#xff0c;有一种代码被称为"屎山代码"。这并非指某种编程语言或方法&#xff0c;而是对那些庞大而复杂的项目的一种形象称呼。屎山代码&#xff0c;也被称为"祖传代码"&#xff0c;是历史遗留问题&#xff0c;是前人留给我们的"宝藏"…

性能测试2【搬代码】

1.性能测试脚本完善以及增强 2.jmeter插件安装以及监控使用 3.性能压测场景设置&#xff08;基准、负载、压力、稳定性&#xff09; 4. 无界面压测场景详解 一、性能测试脚本完善以及增强 使用控制器的目的是使我们的脚本更加接近真实的场景 1.逻辑控制器: 【事务控制器】&…

电商API接口接入||电商比价项目比价系统搭建需要注意哪些?

在搭建一个淘宝/京东比价系统时&#xff0c;需要注意以下几个方面&#xff0c;以确保系统的有效性、准确性和用户友好性&#xff1a; 确定平台和商品范围&#xff1a; 明确系统覆盖的电商平台&#xff0c;如淘宝、京东等。确定要比较的商品类别和范围&#xff0c;以确保数据的…

Maven环境搭建

&#x1f4bb;博主现有专栏&#xff1a; C51单片机&#xff08;STC89C516&#xff09;&#xff0c;c语言&#xff0c;c&#xff0c;离散数学&#xff0c;算法设计与分析&#xff0c;数据结构&#xff0c;Python&#xff0c;Java基础&#xff0c;MySQL&#xff0c;linux&#xf…

【Vue】核心概念 - module

目标 掌握核心概念 module 模块的创建 问题 由于使用单一状态树&#xff0c;应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时&#xff0c;store 对象就有可能变得相当臃肿。 这句话的意思是&#xff0c;如果把所有的状态都放在state中&#xff0c;当项目变得…

表的设计与查询

目录 一、表的设计 1.第一范式&#xff08;一对一&#xff09; 定义&#xff1a; 示例&#xff1a; 2.第二范式&#xff08;一对多&#xff09; 定义&#xff1a; 要求&#xff1a; 示例&#xff1a; 3.第三范式&#xff08;多对多&#xff09; 定义&#xff1a; 要求…

Selenium三种等待方式的使用!

UI自动化测试&#xff0c;大多都是通过定位页面元素来模拟实际的生产场景操作。但在编写自动化测试脚本中&#xff0c;经常出现元素定位不到的情况&#xff0c;究其原因&#xff0c;无非两种情况&#xff1a;1、有frame&#xff1b;2、没有设置等待。 因为代码运行速度和浏览器…

如何有效释放Docker占用的存储空间

随着Docker的广泛应用&#xff0c;我们经常会遇到Docker占用过多存储空间的问题。这可能是由于频繁的镜像拉取、容器创建和删除等操作导致的。本文将介绍几种方法来有效释放Docker占用的存储空间&#xff0c;特别是docker system prune命令的使用。 Docker的存储机制 Docker使…