Skip to content

关于spring那些不得不说的故事

Published: at 18:16

Spring Security (6.2.x) Custom Usage


import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;
@Component
public class AkSkAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
    // 进行实际的用户信息校验
    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        System.out.println(authentication.getName());
        List<String> perms = new ArrayList<>(3);
        perms.add("operator");
        perms.add("viewer");
        perms.add("admin");
        List<SimpleGrantedAuthority> authorities = new ArrayList<>(perms.size());
        for (String perm:perms) {
            authorities.add(new SimpleGrantedAuthority(perm));
        }
        return new MyToken("ak","sk",authorities);
    }
    // 判断当前Authentication 是否会用该Provider校验
    @Override
    public boolean supports(Class<?> authentication) {
        return MyToken.class.isAssignableFrom(authentication);
    }
}

import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

public class AkSkAuthenticationFilter extends AbstractAuthenticationProcessingFilter {
    public AkSkAuthenticationFilter(String defaultFilterProcessesUrl, AuthenticationManager authenticationManager) {
        super(new AntPathRequestMatcher(defaultFilterProcessesUrl, "GET"));
        this.setAuthenticationManager(authenticationManager);
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
        String ak = request.getHeader("ak");
        String sk = request.getHeader("sk");
        if ("ak".equals(ak) && "sk".equals(sk)) {
            List<String> perms = new ArrayList<>(3);
            perms.add("operator");
            perms.add("viewer");
            perms.add("admin");
            List<SimpleGrantedAuthority> authorities = new ArrayList<>(perms.size());
            for (String perm:perms) {
                authorities.add(new SimpleGrantedAuthority(perm));
            }
            // 调用Provider校验
            return this.getAuthenticationManager().authenticate(new MyToken(ak,sk,authorities));
        }

        return null;
    }

    // 成功
    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
        System.out.println("success");
        final String xId = UUID.randomUUID().toString();
        response.getWriter().write(xId);
    }

    @Override
    protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response,
                                              AuthenticationException failed)
            throws IOException, ServletException {
        System.out.println("fail");
        super.unsuccessfulAuthentication(request, response, failed);
    }
}

import com.example.demo.sec.AkSkFilter;
import com.example.demo.sec.AkSkAuthenticationProvider;
import com.example.demo.sec.AkSkAuthenticationFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.ProviderManager;
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.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.csrf.CsrfFilter;

import static org.springframework.security.config.http.SessionCreationPolicy.STATELESS;

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfiguration {

    @Autowired
    private AkSkFilter akSkFilter;

    // AbstractAuthenticationProcessingFilter
    @Bean
    public AkSkAuthenticationFilter theAuthFilter() {
        return new AkSkAuthenticationFilter("/v1/token",authenticationManager());
    }

    // 包装 AuthenticationProvider
    @Bean
    public AuthenticationManager authenticationManager(){
        ProviderManager authenticationManager = new ProviderManager(authenticationProvider());
        authenticationManager.setEraseCredentialsAfterAuthentication(true);
        return authenticationManager;
    }
    // AuthenticationProvider
    @Bean
    public AuthenticationProvider authenticationProvider() {
        return new AkSkAuthenticationProvider();
    }



    @Bean
    public SecurityFilterChain authorizeRequestsFilterChain(HttpSecurity http) throws Exception {
        http.csrf(AbstractHttpConfigurer::disable)
                .authorizeHttpRequests(req ->
                        req.requestMatchers("/v1/token")
                                .permitAll()
                                .anyRequest().authenticated()

                )
                 // 设置authenticationManager
                .authenticationManager(authenticationManager())
                .sessionManagement(session -> session.sessionCreationPolicy(STATELESS))
                //  设置自定义的校验
                .addFilterAfter(theAuthFilter(), CsrfFilter.class)
                .addFilterBefore(akSkFilter, AkSkAuthenticationFilter.class);

        return http.build();
    }

Spring IoC and Bean

The org.springframework.beans and org.springframework.context packages are the basis for Spring Framework’s IoC container. The BeanFactory interface provides an advanced configuration mechanism capable of managing any type of object. ApplicationContext is a sub-interface of BeanFactory. It adds:

  • Easier integration with Spring’s AOP features
  • Message resource handling (for use in internationalization)
  • Event publication
  • Application-layer specific contexts such as the WebApplicationContext for use in web applications.

The Spring IoC container

Create Config Get Beans

ApplicationContext context1 = new ClassPathXmlApplicationContex("services.xml");
ApplicationContext context2 = new GenericGroovyApplicationContext("services.groovy");
ApplicationContext context3 = FileSystemXmlApplicationContext("")
ApplicationContext context = new ClassPathXmlApplicationContext("services.xml");

// getBean
DemoEntity entity1 = context.getBean("entity1", DemoEntity.class);

System.out.println(entity1);

DemoEntity entityAlias = context.getBean("entityAlias",DemoEntity.class);
System.out.println(entityAlias);
assert entity1 == entityAlias;

XML config attrs

<?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:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
		https://www.springframework.org/schema/beans/spring-beans.xsd">

        <!-- factory-method -> public static factory method  -->
    <bean id="entity2" class="tech.realcpf.spring.dto.DemoEntity" factory-method="createInstance">
        <property name="name" value="jiacheng.liu"/>
        <property name="age" value="25"/>
    </bean>


    <!-- use    xmlns:p="http://www.springframework.org/schema/p"-->
    <bean id="entity3" class="tech.realcpf.spring.dto.DemoEntity"
        destroy-method="destroy"
        p:name="jc.liu"
        p:age="89">
        <!-- additional collaborators and configuration for this bean go here -->
    </bean>

    <!-- constructor-arg -->
    <bean id="entity1" class="tech.realcpf.spring.dto.DemoEntity">
        <constructor-arg type="java.lang.String" value="by constructor" name="name" index="0"></constructor-arg>
        <constructor-arg type="java.lang.Integer" value="123" name="age" index="1"></constructor-arg>
    </bean>

    <alias name="entity1" alias="entityAlias"></alias>

    <!-- depends-on  other beans -->
   <bean id="beanOne" class="ExampleBean" depends-on="manager"/>
<!--    <bean id="manager" class="ManagerBean" />-->

    <!-- lazy-init  -->
   <bean id="lazy" class="com.something.ExpensiveToCreateBean" lazy-init="true"/>
<!--    <bean name="not.lazy" class="com.something.AnotherBean"/>-->

    <!-- init method -->
   <bean id="exampleInitBean" class="examples.ExampleBean" init-method="init"/>
   <!-- scope -->
   <bean id="exampleInitBean" class="examples.ExampleBean" scope="prototype"/>
</beans>

prototype


singleton

BeanPostProcessor


private static final BeanPostProcessor beanPostProcessor = new BeanPostProcessor() {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("Bean '" + beanName + "' created : " + bean.toString());
        return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
    }
};

// PropertyOverrideConfigurer
// PropertySourcesPlaceholderConfigurer

private static final BeanFactoryPostProcessor beanFactoryPostProcessor = new BeanFactoryPostProcessor() {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {

    }
};

Spring SPI


通过 resources/META-INF/spring.factories 作为SPI的配置文件
内容如下:
org.springframework.context.ApplicationContextInitializer=\
tech.realcpf.spring.DemoApplicationContextInitializer
org.springframework.context.ApplicationListener=\
tech.realcpf.spring.DemoApplicationListener


如果用spring-boot,再spring-boot3之后配置文件改成
 resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 作为SPI的配置文件
内容则是一个类一行

其中 org.springframework.boot.autoconfigure.AutoConfiguration 是Interface名

Spring Aop

https://docs.spring.io/spring-framework/reference/core/aop-api/pointcuts.html

Proxy - ProxyFactoryBean

ApplicationContext context =
        new ClassPathXmlApplicationContext("aopdemo.xml");
IDemoModel model = (IDemoModel) context.getBean("demoModelProxy");

System.out.println(model.hello("jc.liu"));

<bean id="demoModelProxy"
   class="org.springframework.aop.framework.ProxyFactoryBean">
       <property name="proxyInterfaces">
           <list>
               <value>tech.realcpf.spring.aop.IDemoModel</value>
           </list>
       </property>

       <property name="target" ref="demoModelImpl">

       </property>

       <property name="interceptorNames">
           <list>
                <value>demoModelArgs</value> <!-- impl MethodBeforeAdvice -->
               <value>demoModelReturn</value> <!-- impl AfterReturningAdvice -->
           </list>
       </property>
   </bean>

Auto Proxy - BeanNameAutoProxyCreator

ApplicationContext context =
                new ClassPathXmlApplicationContext("aopdemo.xml");
IDemoModel demoModel = context.getBean(IDemoModel.class);
System.out.println(demoModel.hello("jc.liu"));

 <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
                <property name="interceptorNames">
                    <list>
                        <value>demoModelArgs</value>
                        <value>demoModelReturn</value>
                    </list>
                </property>

        <!--        for auto match-->
        <property name="beanNames" value="*Model"></property>
    </bean>

Aspectj


// call before
@Before("tech.realcpf.spring.aop.aj.SystemArchitecture.service()")
public void callBefore(JoinPoint joinPoint) {
    System.out.println("Before : " + Arrays.toString(joinPoint.getArgs()));
}


// call after
@AfterReturning(pointcut = "tech.realcpf.spring.aop.aj.SystemArchitecture.service()",
        returning = "result")
public void logResult(Object result) {
    System.out.println("after :" + result);
}

@Pointcut("execution(* tech.realcpf.spring.aop.aj..*(..))")
public void service() {}

    <!-- enable aspectj -->
    <aop:aspectj-autoproxy/>

#Spring EL

https://docs.spring.io/spring-framework/reference/core/expressions/evaluation.html

更多的语法语义

static class Simple {
    public List<Boolean> booleans = new ArrayList<>();
}

ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression("'Hello World'.concat('!')");
String message = (String) exp.getValue();
System.out.println(message);
System.out.println(
    parser.parseExpression("'hello'.bytes.length").getValue()
);

Simple simple = new Simple();
simple.booleans.add(false);
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();

parser.parseExpression("booleans[0]")
        .setValue(context,simple,"true");
assert simple.booleans.get(0);