Spring Security (6.2.x) Custom Usage
- AuthenticationProvider
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);
}
}
- AbstractAuthenticationProcessingFilter
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);
}
}
- Configuration
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.
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>
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);