后端同学在不使用模板项目(内部已经做了例如用户系统,安全认证,会话管理等,只需要定制自己的业务逻辑,简单的开发很便捷,更新维护和修改原有模块就是得在代码堆中挣扎了)的情况下,首先需要实现的就是一个用户系统,以及相关的安全配置和逻辑。
自己去实现一个完善的用户系统过于复杂,那么我们就涉及到用户系统选型的问题了,这里推荐 Keycloak。Keycloak 实现了 Oidc,Saml 等身份协议,并兼容 Saml,允许定制化,并且开源!
Life is short, show me the code.
这个 demo ( Kicey/keycloak-demo (github.com) )是下载即可运行的,demo 中使用的 Keycloak 部署在我的个人服务器上,在 2025 年之前它将是有效的。运行之后先试试:
- 127.0.0.1:8765
- 127.0.0.1:8765/customer
KeyCloak 相关的配置如下:
设置了 Oidc 必要的一些 Client 配置
keycloak:
realm: demo
auth-server-url: 'https://identity.kicey.site/'
ssl-required: external
resource: keycloak-demo
verify-token-audience: true
credentials:
secret: xCi1aChVriLE13afwZfs5pOpC15RoppI
use-resource-role-mappings: true
confidential-port: 0
Spring Security 的配置如下:
- 使用一个全局的身份认证器(向 spring security AuthenticationManager 注册)
- 指定 Session 策略,具体的 Session 由 spring security 实现,这里相当于只是选择一个配置项
- 配置安全规则
@KeycloakConfiguration
public class WebSecurityConfig extends KeycloakWebSecurityConfigurerAdapter {
/**
* Registers the KeycloakAuthenticationProvider with the authentication manager.
*/
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(keycloakAuthenticationProvider());
}
/**
* Defines the session authentication strategy.
*/
@Bean
@Override
protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
}
@Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
http
.csrf().disable()
.authorizeRequests()
.antMatchers("/customer/**").hasAuthority("CUSTOMER")
.antMatchers("/admin/**").hasAuthority("ADMIN")
.antMatchers("/provider/**").hasAuthority("PROVIDER")
.anyRequest().permitAll();
}
}
下面的 Bean 负责加载 application.yml 中的 Keycloak 配置(默认是 META-INF/keycloak.json)
@Configuration
public class BeanConfig {
@Bean
public KeycloakConfigResolver keycloakConfigResolver(){
return new KeycloakSpringBootConfigResolver();
}
}
在接口中使用身份信息的方式如下
package site.kicey.keycloakdemo.resource;
import org.keycloak.adapters.springsecurity.token.KeycloakAuthenticationToken;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import site.kicey.keycloakdemo.common.SimpleResponse;
import javax.servlet.http.HttpServletRequest;
/**
* @author Kicey
*/
@RestController()
@RequestMapping("/customer")
public class Customer {
@GetMapping
public SimpleResponse getCustomer(HttpServletRequest request, KeycloakAuthenticationToken authentication) {
return new SimpleResponse("目前角色权限:" + authentication.getAuthorities().toString(), request.getRequestURI(),
authentication.getAccount().getKeycloakSecurityContext().getIdToken());
}
}
这是一个标准的 spring 自动注入的方式,也可以使用 SecurityContext (底层由 ThreadLocal 实现)静态方法获取 authentication,方便在 controller 之下的 service 方法中使用而减少不必要的参数传递。
只需要很少的代码,就完成了一个后端系统的身份系统和安全配置,congratulation!
题外话:建立 https 的 TLS/SSL 是一个比较耗时的操作,并且配置也相对麻烦,不建议在每个服务都使用。可行的方式是在网关处使用(spring-cloud-gateway 或者 nginx)。