内容源自很多博客,就不一一列出了
关于本文对spring security的介绍,读者没有一点相关基础是看不懂的,spring boot里面的封装一层套一层,很多东西它都会自己主动去调用,读者没有相关了解很难真正掌握,建议多看一些基础介绍再看本文。
本人也是小白,所以可能有些理解是错的,不过代码是运行过的,确认没有问题。
应用的安全性包括用户认证(Authentication)和用户授权(Authorization)两个部分
1. SecurityContextPersistenceFilter (保存认证信息到SecurityContextHolder) --> SecurityContextHolder (提供对SecurityContext的访问)--> SecurityContext (持有Authentication对象和其他可能需要的信息--当前正在访问系统的用户的详细信息) --> Authentication(存有当前用户的权限)--Spring Security方式的认证主体。
2. UsernamePasswordAuthenticationFilter (处理用户认证逻辑过滤器) --> 获取username 和 password ,实例化为一个Token(令牌:UsernamePasswordAuthenticationToken -- Authentication接口的实例 -- 未认证) -- > 调用getAuthenticationManager,获取AuthenticationManager。
3.AuthenticationManager(身份认证的主要策略设置接口) -- > 只有一个authentication()方法,不直接自己处理认证请求,管理了多个AuthenticationProvider接口 -- > ProviderManagger(默认实现上述接口的类,authentication()方法中for循环遍历寻找合适的Provider)。
4. AuthenticationProvider(处理用户认证的接口) -- > authentication()方法【认证方法】和support()方法【是否支持某类型token的方法】--> AbstractUserDetailsAuthenticationProvider(实现上述接口的具体实现类,支持UsernamePasswordAuthenticationProvider Token类型) --> DaoAuthenticationProvider类(上述的子类)--> 调用getUserDetailsService()获取UserDetailsService对象(loadUserByUsername()方法,返回值为UserDetails--构建Authentication对象必须的信息,可以自定义 )--> 调用loadUserByUsername根据userName获取UserDetail对象(有User非常全面的信息)。
5.对比UserDetail后,Authentication填充完整,认证通过。认证信息保存在SecurityContextHolder中。
SecurityContextHolder.getContext().setAuthentication(authentication)
【注】对拦截器的理解:https://blog.csdn.net/Fern2018/article/details/87870077
一、User类--继承UserDetails
package com.dongnao.james.model.Sys;
import com.dongnao.james.model.Role;
import com.dongnao.james.model.Sys.SysPermission;
import com.dongnao.james.model.Sys.SysRole;
import com.dongnao.james.model.User;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import javax.persistence.*;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@Table(name = "users")
@Entity
//实现UserDetail接口,用户实体为spring security所使用的用户
public class SysUser extends UserDetails {
@Id
@GeneratedValue
private Long userId; //用户id
private String userName; //用户名
private String password; //用户密码
private boolean enabled; //账户是否可用
//通过关联的方式映射用户和角色多对多的关系
// cascade表示级联操作 CascadeType.REFRESH级联刷新--会重新查询数据库里的最新数据 FetchType.EAGER急加载--加载一个实体时,急加载的属性会立即从数据库中加载
@ManyToMany(cascade = {CascadeType.REFRESH}, fetch = FetchType.EAGER) //(驼峰映射)
@JoinTable(name = "usersRoles",
joinColumns = {@JoinColumn(name = "userId", referencedColumnName = "userId")},
inverseJoinColumns = {@JoinColumn(name = "roleId", referencedColumnName = "roleId")})
private List<SysRole> roles;
public Long getUserId() {
return userId;
}
public void setUserId(Long id) {
this.userId = id;
}
public void setUserName(String username) {
this.userName = username;
}
public void setPassword(String password) {
this.password = password;
}
public List<SysRole> getRoles() {
return roles;
}
public void setRoles(List<SysRole> roles) {
this.roles = roles;
}
@Override
public String getPassword() {
return this.password;
}
@Override
public String getUsername() {
return this.userName;
}
// 账户未过期
@Override
public boolean isAccountNonExpired() {
return true;
}
//账户未锁定
@Override
public boolean isAccountNonLocked() {
return true;
}
//密码未过期
@Override
public boolean isCredentialsNonExpired() {
return true;
}
//账户可用
@Override
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
@Override
//重写getAuthorities()方法,将用户的角色作为权限
public Collection<? extends GrantedAuthority> getAuthorities() {
List<GrantedAuthority> auths = new ArrayList<>();
List<SysRole> roles = this.getRoles();
for (SysRole role : roles) {
auths.add(new SimpleGrantedAuthority(role.getRoleName()));
}
return auths;
}
// @Override
// //重写getAuthorities()方法,将uri作为权限
// public Collection<? extends GrantedAuthority> getAuthorities() {
// List<GrantedAuthority> auths = new ArrayList<>();
// List<SysRole> roles = this.getRoles();
// for (SysRole role : roles) {
// List<SysPermission> permissions = role.getPermissions();
// for(SysPermission permission : permissions){
// auths.add(new SimpleGrantedAuthority(permission.getUri()));
// }
// }
// return auths;
// }
}
GrantedAuthority接口 --> 对认证主题的应用层面的授权,含当前用户的权限信息 --> 只有一个getAuthority()方法。
SimpleGrantedAuthority是上述接口的实现类
二、UserDetailsService类 -- loadUserByUsername方法
package com.dongnao.james.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import com.dongnao.james.model.Sys.SysUser;
import com.dongnao.james.utils.SQLRepository.SysUserRepository;
import org.springframework.stereotype.Service;
//自定义的时候需要实现一下UserDetailsService接口
@Service
public class UserService implements UserDetailsService {
@Autowired
SysUserRepository userRepository;
@Override
//重写loadUserByUsername方法获得用户,返回给定用户的UserDetails对象
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//通过JPA来根据用户名查询用户信息
SysUser user = userRepository.findByUserName(username);
if (user == null) {
throw new UsernameNotFoundException("用户名不存在");
}
System.out.println("用户密码:"+user.getPassword());
//SysUser已实现UserDetail接口,可直接返回给spring security使用
return user;
}
}
三、Role类
package com.dongnao.james.model.Sys;
import com.dongnao.james.model.Role;
import com.dongnao.james.model.Sys.SysPermission;
import javax.persistence.*;
import java.util.List;
@Table(name="roles")
@Entity
public class SysRole {
@Id
@GeneratedValue
private Long roleId; //角色id
private String roleName; //角色名
@ManyToMany(cascade = {CascadeType.REFRESH},fetch = FetchType.EAGER)
@JoinTable(name = "rolesPermission",
joinColumns = {@JoinColumn(name = "roleId",referencedColumnName = "roleId")},
inverseJoinColumns = {@JoinColumn(name = "perId",referencedColumnName = "perId")})
private List<SysPermission> permissions;
public Long getRoleId() {
return roleId;
}
public void setRoleId(Long id) {
this.roleId = id;
}
public String getRoleName() {
return roleName;
}
public void setRoleName(String name) {
this.roleName = name;
}
public List<SysPermission> getPermissions() {
return permissions;
}
public void setPermissions(List<SysPermission> permissions) {
this.permissions = permissions;
}
}
Permission类
package com.dongnao.james.model.Sys;
import javax.persistence.*;
import java.util.List;
@Entity
@Table(name = "permission")
public class SysPermission {
@Id
@GeneratedValue
private Long perId; //权限id
private String perName; //权限名
private String description; //描述
private String uri; //对应的uri
public Long getPerId() {
return perId;
}
public void setPerId(Long perId) {
this.perId = perId;
}
public String getPerName() {
return perName;
}
public void setPerName(String perName) {
this.perName = perName;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getUri() {
return uri;
}
public void setUri(String uri) {
this.uri = uri;
}
}
四、数据访问层 -- jpa
package com.dongnao.james.utils.SQLRepository;
import org.springframework.data.jpa.repository.JpaRepository;
import com.dongnao.james.model.Sys.SysUser;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
//数据访问,根据用户名使用JPA查出用户
public interface SysUserRepository extends JpaRepository<SysUser, Long> {
SysUser findByUserName(String username);
SysUser findByUserId(Long userId);
List<SysUser> findAll();
void delete(Long userId);
SysUser saveAndFlush(SysUser user);
@Transactional
@Modifying
@Query(value = "update users set user_name= :userName,password= :password,enabled=:enabled where user_id = :userId",nativeQuery = true)
void update(@Param("userId") Long userId, @Param("userName") String userName, @Param("password") String password, @Param("enabled") boolean enabled);
}
五、自定义了一个springSecurity安全框架的配置类 继承WebSecurityConfigurerAdapter
package com.dongnao.james.config;
import com.dongnao.james.interceptor.MyFilterSecurityInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
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.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import com.dongnao.james.service.UserService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
@Configuration
@EnableWebSecurity
//如果要自定义扩展springsecurity配置需要继承WebSecurityConfigurerAdapter
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private MyFilterSecurityInterceptor myFilterSecurityInterceptor;
//注册customUserService实例
@Bean
UserDetailsService customUserService() {
return new UserService();
}
//配置全局认证相关的信息
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//添加我们自定义的userdetailservice 认证
auth.userDetailsService(customUserService()).passwordEncoder(new PasswordEncoder() {
@Override
public String encode(CharSequence rawPassword) {
return MD5Util.encode((String) rawPassword);
}
@Override
public boolean matches(CharSequence rawPassword, String encodedPassword) {
return encodedPassword.equals(MD5Util.encode((String) rawPassword));
}
});
}
//具体的权限规则配置
@Override
protected void configure(HttpSecurity http) throws Exception {
//请求权限配置
http.authorizeRequests()
//其余任何路径请求都需要认证(登录后)才能访问
.anyRequest().authenticated()
//通过fromLogin方法来定制登录操作,登录页面公开,不需要认证即可访问
.and()
.formLogin()
.loginPage("/login")
.successForwardUrl("/index")
.failureUrl("/login?error")
.permitAll()
//通过logout方法来定制注销操作,//注销公开,不需要认证即可注销
.and()
.logout()
.logoutSuccessUrl("/loginOut")
.permitAll();
//http.addFilterBefore(myFilterSecurityInterceptor, FilterSecurityInterceptor.class);
}
}
六、针对框架个性化定制
package com.dongnao.james.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
@Configuration
public class SpringMvcConfig extends WebMvcConfigurerAdapter { //Spring内部的一种配置方式,针对框架个性化定制
/**
* 视图跳转控制器
**/
@Override
public void addViewControllers(ViewControllerRegistry registry) {
//访问 /login转向login.html页面
registry.addViewController("/login").setViewName("login");
//访问 /loginOut转向注销loginOut.html页面
registry.addViewController("/loginOut").setViewName("loginOut");
}
/**
* 添加拦截器
**/
// @Override
// public void addInterceptors(InterceptorRegistry registry) {
// // addPathPatterns 用于添加拦截规则
// // excludePathPatterns 用户排除拦截
// registry.addInterceptor(new AuthenticationInterceptor()).excludePathPatterns("/jamesStatic/*")
// .addPathPatterns("/**");
// }
}
七、MD5加密
package com.dongnao.james.config;
import java.security.MessageDigest;
public class MD5Util {
private static final String SALT = "tamboo";
public static String encode(String password) {
password = password + SALT;
MessageDigest md5 = null;
try {
//返回实现指定摘要算法的 MessageDigest 对象。MD5-- 所请求算法的名称
md5 = MessageDigest.getInstance("MD5");
} catch (Exception e) {
throw new RuntimeException(e);
}
char[] charArray = password.toCharArray();
byte[] byteArray = new byte[charArray.length];
for (int i = 0; i < charArray.length; i++)
byteArray[i] = (byte) charArray[i];
byte[] md5Bytes = md5.digest(byteArray);
StringBuffer hexValue = new StringBuffer();
for (int i = 0; i < md5Bytes.length; i++) {
int val = ((int) md5Bytes[i]) & 0xff;
if (val < 16) {
hexValue.append("0");
}
hexValue.append(Integer.toHexString(val)); //十六进制
}
return hexValue.toString();
}
public static void main(String[] args) {
System.out.println(MD5Util.encode("abel"));
}
}
八、pom.xml
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.james</groupId>
<artifactId>consumer</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>consumer</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.3.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<thymeleaf.version>3.0.8.RELEASE</thymeleaf.version>
<thymeleaf-layout-dialect.version>2.2.2</thymeleaf-layout-dialect.version>
<thymeleaf-extras-springsecurity4.version>3.0.2.RELEASE</thymeleaf-extras-springsecurity4.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity4</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.40</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
九、login.html
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8"/>
<title>登录</title>
<link rel="stylesheet" th:href="@{css/bootstrap.min.css}"/>
<link rel="stylesheet" th:href="@{css/signin.css}"/>
<style type="text/css">
body {
padding-top: 50px;
}
.starter-template {
padding: 40px 15px;
text-align: center;
}
</style>
</head>
<body>
<div class="container">
<div class="starter-template">
<!---<p th:if="${param.logout}" class="bg-warning">已注销</p>
<p th:if="${param.error}" class="bg-danger">有错误,请重试</p>--->
<h2>使用账号密码登录</h2>
<form class="form-signin" role="form" name="form" th:action="@{/login}" action="/login" method="post">
<div class="form-group">
<label for="username">账号</label>
<input id="username" type="text" class="form-control" name="username" value="" placeholder="账号"/>
</div>
<div class="form-group">
<label for="password">密码</label>
<input id="password" type="password" class="form-control" name="password" placeholder="密码"/>
</div>
<input type="submit" id="login" value="Login" class="btn btn-primary"/>
</form>
</div>
</div>
</body>
</html>
index.html
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml"
xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4">
<head>
<meta charset="UTF-8"/>
<title sec:authentication="name">index</title>
<link rel="stylesheet" th:href="@{css/bootstrap.min.css}"/>
<style type="text/css">
body {
padding-top: 50px;
}
.starter-template {
padding: 40px 15px;
text-align: center;
}
</style>
</head>
<body>
<div class="container">
<div class="starter-template">
<p>登录名:<span sec:authentication="name"></span></p>
<div sec:authentication="principal.authorities"></div>
<!--<p th:if="${param.logout}" class="bg-warning">已注销</p>-->
<h1 th:text="${msg.title}"></h1>
<p class="bg-primary" th:text="${msg.content}"></p>
<div sec:authorize="hasRole('ROLE_ADMIN')">
<p class="bg-info" th:text="${msg.info}"></p>
</div>
<div sec:authorize="hasRole('ROLE_USER')">
<p class="bg-info">无更多显示信息</p>
</div>
<p>Click <a th:href="@{/home}">here</a> go to home page.</p>
<p>Click <a th:href="@{/admin}">here</a> go to admin page.</p>
<form th:action="@{/logout}" method="post">
<input type="submit" class="btn btn-primary" value="注销"/>
</form>
</div>
</div>
</body>
</html>
十、application.properties
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8
spring.datasource.username=root
spring.datasource.password=123
logging.level.org.springframework.security=info
spring.thymeleaf.cache=false
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
#spring.jackson.serialization.indent_output=true
spring.thymeleaf.prefix=classpath:/jamesHtml/
spring.resources.static-locations=classpath:/jamesStatic/
#spring.thymeleaf.mode=HTML
#数据表命名下划线问题
#spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
十一、添加拦截器
FilterInvocationSecurityMetadataSource
package com.dongnao.james.interceptor;
import com.dongnao.james.model.Sys.SysPermission;
import com.dongnao.james.model.Sys.SysRole;
import com.dongnao.james.utils.SQLRepository.SysPermissionRepository;
import com.dongnao.james.utils.SQLRepository.SysRoleRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletRequest;
import java.util.*;
@Service
public class MyInvocationSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {
@Autowired
private SysPermissionRepository permissionRepository;
@Autowired
private SysRoleRepository roleRepository;
private HashMap<String, Collection<ConfigAttribute>> map = null;
/**
* 加载权限表中所有权限
*/
public void loadResourceDefine() {
map = new HashMap<>();
Collection<ConfigAttribute> array;
ConfigAttribute cfg;
List<SysRole> roles = roleRepository.findAll();
for (SysRole role : roles){
List<SysPermission> permissions = role.getPermissions();
array = new ArrayList<>();
cfg = new SecurityConfig(role.getRoleName());
array.add(cfg);
for (SysPermission permission : permissions) {
//用权限的getUrl() 作为map的key,用ConfigAttribute的集合作为 value,
map.put(permission.getUri(), array);
}
}
}
//此方法是为了判定用户请求的url 是否在权限表中,如果在权限表中,则返回给 decide 方法,用来判定用户是否有此权限。如果不在权限表中则放行。
@Override
public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
if (map == null)
loadResourceDefine();
//object 中包含用户请求的request 信息
HttpServletRequest request = ((FilterInvocation) object).getHttpRequest();
//匹配HttpServletRequest的简单策略接口RequestMatcher的实现类AntPathRequestMatcher
AntPathRequestMatcher matcher;
String resUri;
for (Iterator<String> iter = map.keySet().iterator(); iter.hasNext(); ) {
resUri = iter.next();
matcher = new AntPathRequestMatcher(resUri);
if (matcher.matches(request)) {
return map.get(resUri);
}
}
return null;
}
@Override
public Collection<ConfigAttribute> getAllConfigAttributes() {
return null;
}
@Override
public boolean supports(Class<?> clazz) {
return true;
}
}
AccessDecisionManager
package com.dongnao.james.interceptor;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.stereotype.Service;
import java.util.Collection;
import java.util.Iterator;
@Service
public class MyAccessDecisionManager implements AccessDecisionManager {
// decide 方法是判定是否拥有权限的决策方法,参数object 包含客户端发起的请求的requset信息
@Override
public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException {
if(null == configAttributes || configAttributes.size() <= 0){
return;
}
ConfigAttribute c;
String needRole;
for(Iterator<ConfigAttribute> iter = configAttributes.iterator();iter.hasNext();){
c = iter.next();
needRole = c.getAttribute();
System.out.println("acc 需要的角色:"+needRole);
for(GrantedAuthority ga : authentication.getAuthorities()){
if(needRole.trim().equals(ga.getAuthority())){
return;
}else if(ga.getAuthority().equals("ROLE_ANONYMOUS")){
// 用户没有登陆!
return ;
}
}
}
throw new AccessDeniedException("You don't have right");
}
@Override
public boolean supports(Class<?> aClass) {
return true;
}
@Override
public boolean supports(ConfigAttribute configAttribute) {
return true;
}
}
MyFilterSecurityInterceptor --> FilterSecurityInterceptor extends AbstractSecurityInterceptor implements Filter
package com.dongnao.james.interceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.SecurityMetadataSource;
import org.springframework.security.access.intercept.AbstractSecurityInterceptor;
import org.springframework.security.access.intercept.InterceptorStatusToken;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.stereotype.Service;
import javax.servlet.*;
import java.io.IOException;
@Service
public class MyFilterSecurityInterceptor extends AbstractSecurityInterceptor implements Filter {
@Autowired
private FilterInvocationSecurityMetadataSource securityMetadataSource;
@Autowired
public void setAccessDecisionManager(MyAccessDecisionManager myAccessDecisionManager) {
super.setAccessDecisionManager(myAccessDecisionManager);
}
@Override
public SecurityMetadataSource obtainSecurityMetadataSource() {
return this.securityMetadataSource;
}
@Override
public void destroy() {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
FilterInvocation filterInvocation = new FilterInvocation(servletRequest,servletResponse,filterChain);
invoke(filterInvocation);
}
public void invoke(FilterInvocation filterInvocation) throws IOException,ServletException{
/**
* filterInvocation里面有一个被拦截的url
* 里面调用MyInvocationSecurityMetadataSource的getAttributes(Object object)这个方法获取fi对应的所有权限
* 再调用MyAccessDecisionManager的decide方法来校验用户的权限是否足够访问
*/
InterceptorStatusToken token = super.beforeInvocation(filterInvocation);
try{
//执行下一个拦截器
filterInvocation.getChain().doFilter(filterInvocation.getRequest(),filterInvocation.getResponse());
}finally {
super.afterInvocation(token,null);
}
}
@Override
public Class<?> getSecureObjectClass() {
return FilterInvocation.class;
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
}
或者采用如下方法