Spring Security
Spring Security is a security framework that can provide a comprehensive security access control solution for J2EE projects. It relies on servlet filters. These filters intercept incoming requests and do some security processing before the application processes the request.
Spring Security intercepts user requests as follows:
- through the interceptor stack
- Pre intercept before accessing methods
- Post interception after method access ends
Among them, the interception of the interceptor stack is mainly used to prevent malicious attacks, user session expiration, and rough permission interception. The diagram of the interceptor stack is as follows:
Pre and Post perform real permission interception. They are added in the form of annotations before a class or a method. They mainly have the following four annotations:
- @PreAuthorize Whether the current user has permission to call this method
- @PostAuthorize What to do after the method is called
- @PreFilter filters before method invocation
- @PosFilter When the method is called, it will filter out the return value that does not meet the conditions
1. In the interceptor stack, we mainly configure AuthenticationProcessingFilter
and FilterSecurityInterceptor
these two filters. The relationship between these classes is shown in the following figure:
AuthenticationProcessingFilter
It is mainly used to process the form login request. We mainly rewrite the function of the filter by implementing AuthenticationProvider
and these two classes. The configuration of the interceptor is configured by inheritance , and we also configure the filter in it. First of all, let's see that it inherits and is the main entrance to configure the interceptor. The code of the interceptor is as follows:UserDetailsService
FilterSecurityInterceptor
WebSecurityConfigurerAdapter
AuthenticationProcessingFilter
SecurityConfig
WebSecurityConfigurerAdapter
public class SecurityConfig extends WebSecurityConfigurerAdapter{
@Autowired
private MyAuthenticationProvider authenticationProvider;//该类实现了用户登录的逻辑
@Autowired
private CustomAuthenticationSuccessHandler authenticationSuccessionHandler;//对用户成功登录后的处理
@Override
protected void configure(AuthenticationManagerBuilder auth)
throws Exception {
auth.authenticationProvider(authenticationProvider);//配置用户登录过滤器
}
@Override
protected void configure(HttpSecurity http) throws Exception {//FilterSecurityInterceptor拦截器做配置
http.authorizeRequests()
.antMatchers("/login").permitAll()
.requestMatchers(CorsUtils::isPreFlightRequest).permitAll()//处理跨域请求中的Preflight请求
.anyRequest().authenticated()
.and().formLogin().permitAll()
.and().formLogin().successHandler(authenticationSuccessionHandler)//配置登录成功后的Handler
.and().formLogin().failureUrl("http://localhost:63342/yjsy-ui/build/login/login.html")//配置用户登录失败后的跳转页面
.and().csrf().disable();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
In the user login logic, we can get all the data submitted by the user through the form, then perform logical verification, and UserDetailsService
obtain user permissions through the interface.
2. Pre and Post are written as follows:
@PreAuthorize("hasMethodPrivilege('ExamDelete')")//类似这样
@RequestMapping(value = "/delete-exam", method = DELETE)
@ResponseBody
public Page<Exam> delete(@RequestParam int id,
@RequestParam(value = "page",defaultValue = PAGE_PAGE) Integer page,
@RequestParam(value = "size",defaultValue = PAGE_SIZE) Integer size) {
service.delete(id);
return this.getPage(page, size);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
It can be seen that in the @PreAuthorize
method, we call the method in the form of a string. hasMethodPrivilege
The parameter of this method is a String
type parameter, and we can also pass hasMethodPrivilege('#id')
in the parameters sent by the front end in the form of pass. Spring Security will then invoke the method through reflection.
So where should we write this method?
We need a configuration method MethodSecurityConfig
, which is inherited from GlobalMethodSecurityConfiguration
, where the expression is configured.
The relationship between these classes is shown in the following figure:
First, let's look at the MethodSecurityConfig
classes:
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration{
@Autowired
private CustomPermissionEvaluator cpe;//用来重写hasPermission表达式
@Autowired
private CustomMethodSecurityExpressionHandler expressionHandler;//用来配置表达式
@Override
protected MethodSecurityExpressionHandler createExpressionHandler() {
expressionHandler.setPermissionEvaluator(cpe);
return expressionHandler;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
CustomPermissionEvaluator
Classes are mainly used to override hasPermiss
methods, and the real configuration is CustomMethodSecurityExpressionHandler
in.
Here's the code we look CustomMethodSecurityExpressionHandler
at:
@Service
public class CustomMethodSecurityExpressionHandler
extends DefaultMethodSecurityExpressionHandler {
private final AuthenticationTrustResolver trustResolver = new AuthenticationTrustResolverImpl();
@Autowired
private MySecurityExpressionRoot root;//我们要在这个类中写表达式
@Autowired
private CustomPermissionEvaluator cpe;//还是hasPermission的表达式
@Override
protected MethodSecurityExpressionOperations createSecurityExpressionRoot(
Authentication authentication, MethodInvocation invocation) {
root.setAuthentication(authentication);
root.setPermissionEvaluator(cpe);
root.setTrustResolver(this.trustResolver);
root.setRoleHierarchy(getRoleHierarchy());
return root;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
See MySecurityExpressionRoot
the class below:
@Component
public class MySecurityExpressionRoot
implements MethodSecurityExpressionOperations {
public final boolean permitAll = true;
public final boolean denyAll = false;
private String defaultRolePrefix = "ROLE_";
protected Authentication authentication;
private AuthenticationTrustResolver trustResolver;
private RoleHierarchy roleHierarchy;
private Set<String> roles;
private PermissionEvaluator permissionEvaluator;
private Object filterObject;
private Object returnObject;
public void setAuthentication(Authentication authentication) {
if (authentication == null) { throw new IllegalArgumentException(
"Authentication object cannot be null"); }
this.authentication = authentication;
}
public boolean hasPagePrivilege(String privilege) {//这就是我们定义的方法,其他方法我们可以拷贝过来,在这里我们可以注入任何Component,具有很大的灵活性
for (GrantedAuthority authority : authentication.getAuthorities()) {
if (authority.getAuthority().equals(privilege)) return true;
}
return false;
}
@Override
public final boolean hasAuthority(String authority) {
throw new RuntimeException("method hasAuthority() not allowed");
}
@Override
public final boolean hasAnyAuthority(String... authorities) {
return hasAnyAuthorityName(null, authorities);
}
@Override
public final boolean hasRole(String role) {
return hasAnyRole(role);
}
@Override
public final boolean hasAnyRole(String... roles) {
return hasAnyAuthorityName(defaultRolePrefix, roles);
}
private boolean hasAnyAuthorityName(String prefix, String... roles) {
final Set<String> roleSet = getAuthoritySet();
for (final String role : roles) {
final String defaultedRole = getRoleWithDefaultPrefix(prefix, role);
if (roleSet.contains(defaultedRole)) { return true; }
}
return false;
}
@Override
public final Authentication getAuthentication() {
return authentication;
}
@Override
public final boolean permitAll() {
return true;
}
@Override
public final boolean denyAll() {
return false;
}
@Override
public final boolean isAnonymous() {
return trustResolver.isAnonymous(authentication);
}
@Override
public final boolean isAuthenticated() {
return !isAnonymous();
}
@Override
public final boolean isRememberMe() {
return trustResolver.isRememberMe(authentication);
}
@Override
public final boolean isFullyAuthenticated() {
return !trustResolver.isAnonymous(authentication)
&& !trustResolver.isRememberMe(authentication);
}
public Object getPrincipal() {
return authentication.getPrincipal();
}
public void setTrustResolver(AuthenticationTrustResolver trustResolver) {
this.trustResolver = trustResolver;
}
public void setRoleHierarchy(RoleHierarchy roleHierarchy) {
this.roleHierarchy = roleHierarchy;
}
private Set<String> getAuthoritySet() {
if (roles == null) {
roles = new HashSet<String>();
Collection<? extends GrantedAuthority> userAuthorities = authentication
.getAuthorities();
if (roleHierarchy != null) {
userAuthorities = roleHierarchy
.getReachableGrantedAuthorities(userAuthorities);
}
roles = AuthorityUtils.authorityListToSet(userAuthorities);
}
return roles;
}
@Override
public boolean hasPermission(Object target, Object permission) {
return permissionEvaluator.hasPermission(authentication, target,
permission);
}
@Override
public boolean hasPermission(Object targetId, String targetType,
Object permission) {
return permissionEvaluator.hasPermission(authentication,
(Serializable) targetId, targetType, permission);
}
public void setPermissionEvaluator(
PermissionEvaluator permissionEvaluator) {
this.permissionEvaluator = permissionEvaluator;
}
private static String getRoleWithDefaultPrefix(String defaultRolePrefix,
String role) {
if (role == null) { return role; }
if ((defaultRolePrefix == null)
|| (defaultRolePrefix.length() == 0)) { return role; }
if (role.startsWith(defaultRolePrefix)) { return role; }
return defaultRolePrefix + role;
}
@Override
public Object getFilterObject() {
return this.filterObject;
}
@Override
public Object getReturnObject() {
return this.returnObject;
}
@Override
public Object getThis() {
return this;
}
@Override
public void setFilterObject(Object obj) {
this.filterObject = obj;
}
@Override
public void setReturnObject(Object obj) {
this.returnObject = obj;
}
}
Reprinted from: https://blog.csdn.net/dalangzhonghangxing/article/details/53024640