Getting Started with Spring Security

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: 
  write picture description here

  1. through the interceptor stack
  2. Pre intercept before accessing methods
  3. 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: 
  write picture description here
   
  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:

  1. @PreAuthorize Whether the current user has permission to call this method
  2. @PostAuthorize What to do after the method is called
  3. @PreFilter filters before method invocation
  4. @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 AuthenticationProcessingFilterand FilterSecurityInterceptorthese two filters. The relationship between these classes is shown in the following figure: 
  write picture description here

  AuthenticationProcessingFilterIt is mainly used to process the form login request. We mainly rewrite the function of the filter  by implementing AuthenticationProviderand 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
  FilterSecurityInterceptorWebSecurityConfigurerAdapterAuthenticationProcessingFilter
SecurityConfigWebSecurityConfigurerAdapter

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 UserDetailsServiceobtain 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 @PreAuthorizemethod, we call the method in the form of a string. hasMethodPrivilegeThe parameter of this method is a Stringtype 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: 
  write picture description here
  First, let's look at the MethodSecurityConfigclasses:

@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

 CustomPermissionEvaluatorClasses are mainly used to override hasPermissmethods, and the real configuration is CustomMethodSecurityExpressionHandlerin. 
 Here's the code we look CustomMethodSecurityExpressionHandlerat: 
  

@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 MySecurityExpressionRootthe 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

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325665838&siteId=291194637