1、跨域配置是指允许跨域请求,前后端分离情况下,都会涉及跨域请求,因此要正确配置服务器的跨域设置,同时要与前端协调,使参数协一致才可以实现访问,全局配置如下:
package com.security; import org.springframework.web.servlet.config.annotation.CorsRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; public class WebMvcConfig implements WebMvcConfigurer{ @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedOrigins("http://localhost:8081") .allowedMethods("GET", "HEAD", "POST", "PUT", "DELETE","OPTIONS") .allowedHeaders("Origin", "X-Requested-With", "Content-Type", "token", "Accept","Access-Control-Request-Method", "Access-Control-Request-Headers") .exposedHeaders("Access-Control-Allow-Origin", "Access-Control-Allow-Credentials") .allowCredentials(true) .maxAge(3600); } }
allowedOrigins是指允许的来源,填写的内容要与前端发送请求一致;allowedMethods指请求方法,options一定要写上,因为get、post等请求之前都会预先做一次options请求;allowedHeaders是指请求头里可以含有信息;exposedHeaders是指返回的响应头部信息含有信息;allowCredentials为true时允许匿名访问;maxAge准备响应前的缓存持续的最大时间(以秒为单位)。
2、Spring Security配置文件,这是spring security的核心,配置代码如下。配置文件中@Autowired 七个对象,这在后续再详述。接下来引入两个Bean,其中一个是用于登录验证,是一个自定义过滤器,后续再详述。另一个用于提供密码加密方法,Bcrypt是一个自带盐的方法,建议用该方法加密。
package com.security; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.ObjectPostProcessor; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.config.annotation.web.configurers.UrlAuthorizationConfigurer; import org.springframework.security.config.http.SessionCreationPolicy; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.web.access.intercept.FilterSecurityInterceptor; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import com.handler.AjaxAccessDeniedHandler; import com.handler.AjaxAuthenticationEntryPoint; import com.handler.AjaxAuthenticationSuccessHandler; import com.handler.AjaxLogoutSuccessHandler; import com.service.MyUserDetailsService; @Configuration public class MySecurityConfig extends WebSecurityConfigurerAdapter{ @Autowired private MyUserDetailsService myUserDetailsService; @Autowired private AjaxAuthenticationEntryPoint authenticationEntryPoint; @Autowired private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter; @Autowired private AjaxLogoutSuccessHandler logoutSuccessHandler; @Autowired private AjaxAccessDeniedHandler accessDeniedHandler; @Autowired private AjaxAuthenticationSuccessHandler authenticationSuccessHandler; @Autowired private CustomSecurityMetadataSource customSecurityMetadataSource; //身份验证的Filter @Bean LoginFilter loginFilter() throws Exception{ LoginFilter loginFilter=new LoginFilter(); loginFilter.setAuthenticationManager(authenticationManagerBean()); loginFilter.setAuthenticationSuccessHandler(authenticationSuccessHandler); return loginFilter; } //选择加密方法Bcrypt方法 @Bean PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Override protected void configure(HttpSecurity http) throws Exception { ApplicationContext applicationContext=http.getSharedObject(ApplicationContext.class); //动态权限资源配置,customSecurityMetadataSource作为配置来源 http.apply(new UrlAuthorizationConfigurer<>(applicationContext)) .withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() { @Override public <O extends FilterSecurityInterceptor> O postProcess(O object) { object.setSecurityMetadataSource(customSecurityMetadataSource); //object.setRejectPublicInvocations(true); //所要请求路径都要在资源里配置才可访问(包括匿名访问) return object; } }); //无状态登录,取消session http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .httpBasic().authenticationEntryPoint(authenticationEntryPoint) //未登录处理 .and() .authorizeRequests() .antMatchers("/login2").permitAll() //.anyRequest() //使用rbac 角色静态方式绑定资源 //.access("@rbacauthorityservice.hasPermission(request,authentication)") //.and() //使用表单登录 //.formLogin().successHandler(authenticationSuccessHandler).failureHandler(authenticationFailureHandler).permitAll() .and() .logout().logoutSuccessHandler(logoutSuccessHandler).permitAll() .and() .csrf().disable(); //勾选“记住我”时 http.rememberMe().rememberMeParameter("remember-me") .userDetailsService(myUserDetailsService).tokenValiditySeconds(300); //身份验证异常处理 http.exceptionHandling().accessDeniedHandler(accessDeniedHandler); //请求资源时,如果带了token令牌,则使用jwt的Authentication进行认证 http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class); // 禁用headers缓存 http.headers().cacheControl(); } }
configure方法是spring security配置的核心。下面这段代码是配置动态权限的关键,其中的customSecurityMetadataSource是一个自定义类,后续将给出代码。object.setRejectPublicInvocations(true);这句的意思是在数据库的menu表里存在的路径才可以访问,这句不能写,因为我们的登录路径/login2需要允许匿名访问,否则任何人都无法访问了。
http.apply(new UrlAuthorizationConfigurer<>(applicationContext)) .withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() { @Override public <O extends FilterSecurityInterceptor> O postProcess(O object) { object.setSecurityMetadataSource(customSecurityMetadataSource); //object.setRejectPublicInvocations(true); //所要请求路径都要在资源里配置才可访问(包括匿名访问) return object; } });
对于已完成登录认证的用户,在请求资源时,不需要二次认证,因此在提交验证之前经过一个自定义Filter,在这个Filter里验证token是否合法,如果合法就可直接通过验证,如果不合法,则进入身份认证。jwtAuthenticationTokenFilter就是自定义的Filter,后续再给出代码。
http.addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
前后端分离的Web应用程序中使用Spring Security+Mybatis+JWT非对称加密+动态权限管理(三):跨域配置、安全配置
原文:https://www.cnblogs.com/wwwzgy/p/14812717.html