参考文章:keyclock单点登陆
springboot+springsecurity+keycloak整合
认识JWT
是一种可以登录系统的实体,可以拥有一些属性,如email、username、address、phone number等,
可以加入组,成为组成员
可以分配角色
识别和验证用户
为用户授权
Keycloak用来识别和验证用户的一些数据,如密码、一次性密码、数字签名、指纹。
角色,用户的一个分类,如管理员、普通用户、管理者、普通雇员等
应用程序一般会将权限分配给指定角色,而不是直接分配给用户。
角色分为Realm级别角色和client级别角色。
用户可以同时拥有Realm角色和不同client的client级别的角色。
一个用户可以与0个或多个角色关联,这些关联关系可以被包含进token或assertions中,applications可以根据这些映射关系来进行访问控制。
一个复合角色可以关联多个普通角色,如复合角色superuser可以关联sales-admin、order-entry-admin角色,如果用户拥有superuser角色,则相当于同时拥有sales-admin和order-entry-admin角色。
组是为了更方便的管理用户
可以为组定义属性
可以为组分配角色
组成员自动继承组的属性和角色。
一个realm管理一系列user、Credentials、roles、groups。
一个user隶属于一个realm
一个user也只能log in 一个realm
Realm彼此之间是隔离的
每个realm只能管理和认证自己控制的user
Clients可以请求keycloak去认证一个user
大多数情况下,clients是一些应用和服务,这些application和service想要通过keycloak加固自己,并提供单点登录解决方案。
Clients还可以仅仅请求认证信息或者访问token,这样他们可以安全的调用其他被keycloak保护的服务。
Client适配器 是一种插件,
这种插件是用来安装在你的应用环境上的,
安装后,可以与keycloak进行通信,并被keycloak保护。
Keycloak针对不同的应用环境提供了不同的适配器,可以下载。
有些应用环境keycloak没有提供apapter,可以使用第三方开发的adapter。
-------------------------------------------------------------
keycloak管理界面点击client,创建client,填写clientID
有效的重定向URI配置指向工单系统的UI首页地址
启用隐式流
web起源设为 *
pom.xml
4.4.0.Final
org.keycloak
keycloak-spring-security-adapter
${keycloak.version}
org.keycloak
keycloak-spring-boot-starter
${keycloak.version}
keycloak.json文件,放在resources目录下
{
"realm": "realm",
"auth-server-url": "http://xxx.xxx:8180/auth",
"ssl-required": "external",
"resource": "myclient",
"public-client": true,
"confidential-port": 0
}
application.properties文件
#keycloak
keycloak.realm=realm
keycloak.resource=myclient
keycloak.auth-server-url=http://xxx.xxx.xxx.xxx:8180/auth
keycloak.ssl-required=external
不废话,直接上代码
@KeycloakConfiguration
@EnableGlobalMethodSecurity(prePostEnabled=true)
public class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter {
@Autowired
private SecurityAuthenticationProvider authenticationProvider;
@Autowired
private KeycloakAuthenticationProcessingFilter keycloakAuthenticationProcessingFilter;
@Autowired
private KeycloakPreAuthActionsFilter keycloakPreAuthActionsFilter;
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authenticationProvider);
}
@Bean
public KeycloakConfigResolver keycloakConfigResolver() {
return new KeycloakSpringBootConfigResolver();
}
@Bean
public FilterRegistrationBean keycloakAuthenticationProcessingFilterRegistrationBean(
KeycloakAuthenticationProcessingFilter filter) {
FilterRegistrationBean registrationBean = new FilterRegistrationBean(filter);
registrationBean.setEnabled(true);
return registrationBean;
}
@Bean
public FilterRegistrationBean keycloakPreAuthActionsFilterRegistrationBean(
KeycloakPreAuthActionsFilter filter) {
FilterRegistrationBean registrationBean = new FilterRegistrationBean(filter);
registrationBean.setEnabled(true);
return registrationBean;
}
@Bean
@Override
protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
return new NullAuthenticatedSessionStrategy();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.cors().and()
.sessionManagement().disable()
.authorizeRequests()
.anyRequest().permitAll();
http
//.addFilterBefore(mySecurityInterceptor, FilterSecurityInterceptor.class)
.addFilterBefore(keycloakAuthenticationProcessingFilter, FilterSecurityInterceptor.class)
.addFilterBefore(keycloakPreAuthActionsFilter, KeycloakAuthenticationProcessingFilter.class);
}
}
通过AccessToken中的用户信息,查询本系统中该用户的权限信息,加入token,具体鉴权方案各系统各不相同,自行实现
@Slf4j
@Component
public class SecurityAuthenticationProvider implements AuthenticationProvider {
@Autowired
private UserService userService;
@Autowired
private RoleService roleService;
private GrantedAuthoritiesMapper grantedAuthoritiesMapper;
public void setGrantedAuthoritiesMapper(GrantedAuthoritiesMapper grantedAuthoritiesMapper) {
this.grantedAuthoritiesMapper = grantedAuthoritiesMapper;
}
/**
* 验证Authentication,建立系统使用者信息principal(token)
*/
@Override
public Authentication authenticate(Authentication authentication) throws RuntimeException {
//从token中获取用户信息
KeycloakAuthenticationToken token = (KeycloakAuthenticationToken) authentication;
AccessToken accessToken = getAccessToken((token));
String userId = accessToken.getSubject();
//查询用户是否存在,若不存在则存入数据库
userService.checkUser(accessToken, userId);
//根据userId查询本系统用户权限,放入token中
List grantedAuthorities = roleService.getGrantedAuthorities(userId);
KeycloakAuthenticationToken authenticationToken =
new KeycloakAuthenticationToken(token.getAccount(),
token.isInteractive(), mapAuthorities(grantedAuthorities));
return authenticationToken;
}
private AccessToken getAccessToken(KeycloakAuthenticationToken principal) {
KeycloakAuthenticationToken token = principal;
KeycloakPrincipal keycloakPrincipal = (KeycloakPrincipal)token.getPrincipal();
KeycloakSecurityContext context = keycloakPrincipal.getKeycloakSecurityContext();
return context.getToken();
}
private Collection extends GrantedAuthority> mapAuthorities(
Collection extends GrantedAuthority> authorities) {
return grantedAuthoritiesMapper != null
? grantedAuthoritiesMapper.mapAuthorities(authorities)
: authorities;
}
@Override
public boolean supports(Class> aClass) {
return KeycloakAuthenticationToken.class.isAssignableFrom(aClass);
}
}