1. 前言
最近有开发小伙伴提了一个有趣的问题。他正在做一个项目,涉及两种风格,一种是给小程序出接口,安全上使用无状态的JWT Token;另一种是管理后台使用的是Freemarker,也就是前后端不分离的Session机制。用Spring Security该怎么办?
2. 解决方案
我们可以通过多次继承WebSecurityConfigurerAdapter构建多个HttpSecurity
。HttpSecurity
对象会告诉我们如何验证用户的身份,如何进行访问控制,采取的何种策略等等。
如果你看过之前的教程,我们是这么配置的:
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 29 30 31 32 33 34 35 36 37 38
|
@Configuration @EnableGlobalMethodSecurity(prePostEnabled = true, jsr250Enabled = true, securedEnabled = true) @EnableWebSecurity @ConditionalOnClass(WebSecurityConfigurerAdapter.class) @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET) public class CustomSpringBootWebSecurityConfiguration {
@Configuration @Order(SecurityProperties.BASIC_AUTH_ORDER) static class DefaultConfigurerAdapter extends WebSecurityConfigurerAdapter {
@Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { super.configure(auth); }
@Override public void configure(WebSecurity web) { super.configure(web); }
@Override protected void configure(HttpSecurity http) throws Exception {
} } }
|
上面的配置了一个HttpSecurity
,我们如法炮制再增加一个WebSecurityConfigurerAdapter
的子类来配置另一个HttpSecurity
。伴随而来的还有不少的问题要解决。
2.1 如何路由不同的安全配置
我们配置了两个HttpSecurity
之后,程序如何让小程序接口和后台接口走对应的HttpSecurity
?
HttpSecurity.antMatcher(String antPattern)
可以提供过滤机制。比如我们配置:
1 2 3 4 5 6
| @Override protected void configure(HttpSecurity http) throws Exception { http.antMatcher("/admin/v1");
}
|
那么该HttpSecurity
将只提供给以/admin/v1
开头的所有URL。这要求我们针对不同的客户端指定统一的URL前缀。
举一反三只要HttpSecurity
提供的功能都可以进行个性化定制。比如登录方式,角色体系等。
2.2 如何指定默认的HttpSecurity
我们可以通过在WebSecurityConfigurerAdapter
实现上使用@Order
注解来指定优先级,数值越大优先级越低,没有@Order
注解将优先级最低。
2.3 如何配置不同的UserDetailsService
很多情况下我们希望普通用户和管理用户完全隔离,我们就需要多个UserDetailsService
,你可以在下面的方法中对AuthenticationManagerBuilder
进行具体的设置来配置UserDetailsService
,同时也可以配置不同的密码策略。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider(); daoAuthenticationProvider.setUserDetailsService(new UserDetailsService() { @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { return null ; } }); BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder(); daoAuthenticationProvider.setPasswordEncoder(bCryptPasswordEncoder); auth.authenticationProvider(daoAuthenticationProvider); }
|
2.4 最终的配置模板
上面的几个问题解决之后,我们基本上掌握了在一个应用中执行多种安全策略。配置模板如下:
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 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
|
@Configuration @EnableGlobalMethodSecurity(prePostEnabled = true, jsr250Enabled = true, securedEnabled = true) @EnableWebSecurity @ConditionalOnClass(WebSecurityConfigurerAdapter.class) @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET) public class CustomSpringBootWebSecurityConfiguration {
@Configuration @Order(1) static class AdminConfigurerAdapter extends WebSecurityConfigurerAdapter {
@Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider(); daoAuthenticationProvider.setUserDetailsService(new UserDetailsService() { @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { return null; } }); BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder(); daoAuthenticationProvider.setPasswordEncoder(bCryptPasswordEncoder); auth.authenticationProvider(daoAuthenticationProvider); }
@Override public void configure(WebSecurity web) { super.configure(web); }
@Override protected void configure(HttpSecurity http) throws Exception { http.antMatcher("/admin/v1") .sessionManagement(Customizer.withDefaults()) .formLogin(Customizer.withDefaults());
} }
@Configuration static class AppConfigurerAdapter extends WebSecurityConfigurerAdapter {
@Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider(); daoAuthenticationProvider.setUserDetailsService(new UserDetailsService() { @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { return null; } }); BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder(); daoAuthenticationProvider.setPasswordEncoder(bCryptPasswordEncoder); auth.authenticationProvider(daoAuthenticationProvider); }
@Override public void configure(WebSecurity web) { super.configure(web); }
@Override protected void configure(HttpSecurity http) throws Exception { http.antMatcher("/app/v1") .sessionManagement(Customizer.withDefaults()) .formLogin(Customizer.withDefaults());
} } }
|
3. 总结
今天我们解决了如何针对不同类型接口采取不同的安全策略的方法,希望对你有用
转载自@felord.cn