SSM使用XML文件配置
WEB.xml
<!--
1:DelegatingFilterProxy用于整合第三方框架(代理过滤器,非真正的过滤器,真正的过滤器需要在spring的配置文件)
整合Spring Security时过滤器的名称必须为springSecurityFilterChain,
否则会抛出NoSuchBeanDefinitionException异常
-->
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
导入 xml 文件
<!-- 导入security-->
<import resource="classpath:spring-security.xml"></import>
spring security配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:security="http://www.springframework.org/schema/security"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security.xsd">
<!--一:定义哪些链接可以放行-->
<!--
http:用于定义相关权限控制
指定哪些资源不需要进行权限校验,可以使用通配符
-->
<security:http security="none" pattern="/js/**"/>
<security:http security="none" pattern="/css/**"/>
<security:http security="none" pattern="/img/**"/>
<security:http security="none" pattern="/plugins/**"/>
<security:http security="none" pattern="/login.html"></security:http>
<!--开启注解方式权限控制-->
<security:global-method-security pre-post-annotations="enabled"/>
<!--
二:定义哪些链接不可以放行,即需要有角色、权限才可以放行
http:用于定义相关权限控制
auto-config:是否自动配置
设置为true时框架会提供默认的一些配置,例如提供默认的登录页面、登出处理等
设置为false时需要显示提供登录表单配置,否则会报错
use-expressions:用于指定intercept-url中的access属性是否使用表达式
-->
<security:http auto-config="true" use-expressions="true">
<!--自定义异常处理-->
<security:access-denied-handler ref="customAccessDeniedHandler"/>
<security:headers>
<!--设置在页面可以通过iframe访问受保护的页面,默认为不允许访问-->
<security:frame-options policy="SAMEORIGIN"></security:frame-options>
</security:headers>
<!--只要认证通过就可以访问-->
<!--
intercept-url:定义一个拦截规则
pattern:对哪些url进行权限控制
access:在请求对应的URL时需要什么权限,默认配置时它应该是一个以逗号分隔的角色列表,
请求的用户只需拥有其中的一个角色就能成功访问对应的URL
isAuthenticated():需要经过认证后才能访问(不是匿名用户)
-->
<security:intercept-url pattern="/pages/**" access="isAuthenticated()"/>
<!--
form-login:定义表单登录信息
-->
<security:form-login login-page="/login.html"
username-parameter="username"
password-parameter="password"
login-processing-url="/login.do"
default-target-url="/pages/main.html"
authentication-failure-url="/login.html"
always-use-default-target="true"
/>
<!--
csrf:对应CsrfFilter过滤器
disabled:是否启用CsrfFilter过滤器,如果使用自定义登录页面需要关闭此项,否则登录操作会被禁用(403)
-->
<security:csrf disabled="true"/>
<!--
logout:退出登录
logout-url:退出登录操作对应的请求路径
logout-success-url:退出登录后的跳转页面
-->
<security:logout logout-url="/logout.do" logout-success-url="/login.html" invalidate-session="true"/>
</security:http>
<!--
三:认证管理,定义登录账号名和密码,并授予访问的角色、权限
authentication-manager:认证管理器,用于处理认证操作
-->
<security:authentication-manager>
<!--
authentication-provider:认证提供者,执行具体的认证逻辑
-->
<security:authentication-provider user-service-ref="springSecurityUserService">
<!--指定密码加密策略-->
<security:password-encoder ref="passwordEncoder"></security:password-encoder>
</security:authentication-provider>
<!-- <security:authentication-provider>-->
<!-- <security:user-service>-->
<!-- <security:user name="admin" authorities="ROLE_ADMIN" password="{noop}admin"></security:user>-->
<!-- </security:user-service>-->
<!-- </security:authentication-provider>-->
<!-- </security:authentication-manager>-->
<!--配置密码加密对象-->
<bean id="passwordEncoder"
class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"/>
</beans>
自定义逻辑处理- 查询用户的权限
package com.hl.travel.security;
import com.hl.travel.Service.LoginService;
import com.hl.travel.Service.UserService;
import com.hl.travel.entity.Permission;
import com.hl.travel.entity.Role;
import com.hl.travel.entity.User;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
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.Component;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
@Component
@RequiredArgsConstructor
public class SpringSecurityUserService implements UserDetailsService {
private final UserService userService;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userService.findUserByUserName(username);
if (user == null) {
return null;
}
List<GrantedAuthority> list = new ArrayList<>();
Set<Role> roles = user.getRoles();
for (Role role : roles) {
Set<Permission> permissions = role.getPermissions();
for (Permission permission : permissions) {
list.add(new SimpleGrantedAuthority(permission.getKeyword()));
}
}
UserDetails userDetails = new org.springframework.security.core.userdetails.User(username, user.getPassword(), list);
return userDetails;
}
}
Spring Boot 配置
Java配置文件
package com.hl.travel.config;
import com.hl.travel.constant.MessageConstant;
import com.hl.travel.security.CustomAccessDeniedHandler;
import com.hl.travel.security.SpringSecurityUserService;
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.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.session.SessionRegistry;
import org.springframework.security.core.session.SessionRegistryImpl;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
import org.springframework.security.web.header.writers.frameoptions.WhiteListedAllowFromStrategy;
import org.springframework.security.web.header.writers.frameoptions.XFrameOptionsHeaderWriter;
import org.springframework.security.web.session.HttpSessionEventPublisher;
import org.springframework.session.Session;
import org.springframework.session.SessionRepository;
import org.springframework.session.data.redis.RedisIndexedSessionRepository;
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
import org.springframework.session.data.redis.config.annotation.web.http.RedisHttpSessionConfiguration;
import org.springframework.session.security.SpringSessionBackedSessionRegistry;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import javax.servlet.ServletException;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)//开启方法级别的权限控制
@Configuration
@EnableRedisHttpSession
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private com.hl.travel.security.SpringSecurityUserService SpringSecurityUserService;
//
// @Autowired
// private RedisIndexedSessionRepository sessionRepository;
//配置后端服务
@Override
protected void configure(HttpSecurity http) throws Exception {
// super.configure(http);
http
.headers().frameOptions().disable()// 解决 in a frame because it set 'X-Frame-Options' to 'deny' 问题 // 开启允许iframe 嵌套
.and()
.cors()//跨域
.configurationSource(corsConfigurationSource())
.and()
.authorizeRequests()
// 其他请求配置
// .antMatchers(MessageConstant.LOGIN_SUCCESS_URL+"/pages/**").authenticated() // 需要认证才能访问
.antMatchers(HttpMethod.OPTIONS, "/**").permitAll() // 允许 OPTIONS 请求通过 用于前端跨域请求 预检请求
.antMatchers("/swagger-ui/**").permitAll() // 允许swagger-ui.html请求通过
.antMatchers("/v3/api-docs/**").permitAll() // 允许/v3/api-docs/**请求通过
.anyRequest().authenticated() // 对其他请求进行身份验证
.and()
.formLogin()
.loginPage(MessageConstant.LOGIN_SUCCESS_URL+"/login.html") // 登录页面
.loginProcessingUrl("/login.do")// 登录请求拦截的url,也就是form表单提交时指定的action
.permitAll() // 允许所有用户访问
.successHandler(loginSuccessHandler()) // 使用自定义的登录成功处理器
.failureUrl(MessageConstant.LOGIN_SUCCESS_URL+"/login.html")// 登录失败后跳转的url
.and()
.logout()
.logoutUrl("/logout.do") // 退出登录拦截的url
.logoutSuccessUrl(MessageConstant.LOGIN_SUCCESS_URL+"/login.html") // 退出登录成功后跳转的url
.and()
.rememberMe() // 开启记住我功能
.and()
.exceptionHandling().accessDeniedHandler(new CustomAccessDeniedHandler())//权限不足处理器
.and()
.csrf()
.disable()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
.invalidSessionUrl(MessageConstant.LOGIN_SUCCESS_URL+"/login.html")//session失效后跳转的url
.maximumSessions(1)//同一账号同时登录最大用户数
.maxSessionsPreventsLogin(false) // 允许新会话替换旧会话
.sessionRegistry(sessionRegistry());// 使用Redis存储会话信息
}
/**
* 解决session失效后 sessionRegistry中session没有同步失效的问题,启用并发session控制,首先需要在配置中增加下面监听器
* @return
*/
@Bean
public HttpSessionEventPublisher httpSessionEventPublisher() {
return new HttpSessionEventPublisher();
}
/**
* 注册bean sessionRegistry
*/
@Bean
public SessionRegistry sessionRegistry() {
return new SessionRegistryImpl();
}
// @Bean
// public SpringSessionBackedSessionRegistry sessionRegistry() {
// return new SpringSessionBackedSessionRegistry<>(sessionRepository);
// }
// 静态资源放行 配置前端服务
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/css/**");
web.ignoring().antMatchers("/js/**");
web.ignoring().antMatchers("/fonts/**");
web.ignoring().antMatchers("/img/**");
web.ignoring().antMatchers("/plugins/**");
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//inMemoryAuthentication 从内存中获取
// auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder()).withUser("admin").password(new BCryptPasswordEncoder().encode("admin")).roles("ADMIN");
//注入userDetailsService的实现类
auth.userDetailsService(SpringSecurityUserService).passwordEncoder(new BCryptPasswordEncoder());
}
@Bean
public AuthenticationSuccessHandler loginSuccessHandler() {
return new SavedRequestAwareAuthenticationSuccessHandler() {
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
// 自定义登录成功后的处理逻辑
//保存一份到前端域名
response.setHeader("Access-Control-Allow-Credentials", "true"); // 允许携带cookie跨域
// 例如,重定向到前端页面的URL
// response.sendRedirect(MessageConstant.LOGIN_SUCCESS_URL+"/pages/main.html");
response.sendRedirect(MessageConstant.LOGIN_SUCCESS_URL+"/pages/main.html");
}
};
}
@Bean
CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowCredentials(true); // 允许携带cookie跨域
configuration.setAllowedOrigins(Arrays.asList(MessageConstant.LOGIN_SUCCESS_URL,MessageConstant.LOGIN_FRONT_URL)); // 允许所有域名进行跨域调用
configuration.setAllowedMethods(Collections.singletonList("*")); // 允许所有请求方法跨域调用
configuration.setAllowedHeaders(Collections.singletonList("*")); // 允许所有请求头跨域调用
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration); // 对所有请求路径进行跨域设置
return source;
}
}
跨域时需要指定域名,不要用*号*
同理配置一下自定义逻辑处理即可