// 项目架构 微服务划分:
// auth认证微服务 实现登录认证拦截,获取token
// gateway 网关微服务
// user用户微服务 用户权限管理
// system系统微服务 核心逻辑处理
// xxx其他微服务
// common模块
//1、 common模块引入keycloak认证相关依赖
<properties>
<keycloak.version>15.0.2</keycloak.version>
</properties>
<dependencies>
<!--SpringCloud OAuth2-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
<version>2.2.1.RELEASE</version>
<exclusions>
<exclusion>
<artifactId>bcprov-jdk15on</artifactId>
<groupId>org.bouncycastle</groupId>
</exclusion>
<exclusion>
<artifactId>HdrHistogram</artifactId>
<groupId>org.hdrhistogram</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-admin-client</artifactId>
<version>${keycloak.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-spring-security-adapter</artifactId>
<version>${keycloak.version}</version>
<exclusions>
<exclusion>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-spring-boot-starter</artifactId>
<version>${keycloak.version}</version>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.keycloak.bom</groupId>
<artifactId>keycloak-adapter-bom</artifactId>
<version>${keycloak.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
// 这里还引入了Spring OAuth2的相关依赖 由于之前系统用的Spring security oauth2做的认证
// 现在将其改为keycloak认证,keycloak对oauth2的兼容很好,通过配置可以轻松实现两种认证随意切换
//2、 auth微服务引入common模块,并添加相关配置:
application.yml:
#keycloak认证配置 (keycloak系统配置)
keycloak:
auth-server-url: http://127.0.0.1:8411/auth/
resource: admin-cli
realm: test #keycloak的realm,最好新建一个,不建议使用master
principal-attribute: preferred_username
use-resource-role-mappings: false #// 是否启用keycloak用户角色映射 可以不启用,使用系统的用户角色映射
ssl-required: none
#keycloak初始化配置(自定义配置*)
kc:
master-realm-user-name: master管理员账号
master-realm-user-password: master管理员密码
target-realm: master
#获取token相关配置 (自定义配置*)
oauth:
tokenUrl: http://127.0.0.1:8411/auth/realms/{替换成自己的域}/protocol/openid-connect/token
kcClientId: test 客户端id
kcClientSecret: 客户端secret

//自定义配置,建议写在配置文件里,也可以写死在代码里 。此时auth微服务已经集成了keycloak
//3、获取token
/**
* keyCloak客户端安全配置 这里可以兼容oauth2
*/
@KeycloakConfiguration
@RequiredArgsConstructor
@EnableGlobalMethodSecurity(prePostEnabled = true)
//@ConditionalOnProperty(value = "oauthModel.type", havingValue = KEY_CLOAK, matchIfMissing = true)
public class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter {
//放开获取token的接口
String[] PERMIT_URL = {"/oauth/**","/keycloak/oauth/token"};
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) {
KeycloakAuthenticationProvider keycloakAuthenticationProvider = keycloakAuthenticationProvider();
SimpleAuthorityMapper grantedAuthorityMapper = new SimpleAuthorityMapper();
// adding proper authority mapper for prefixing role with "ROLE_"
grantedAuthorityMapper.setPrefix("ROLE_");
keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(grantedAuthorityMapper);
auth.authenticationProvider(keycloakAuthenticationProvider);
}
@Bean
@Override
protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
}
@Bean
public org.keycloak.adapters.KeycloakConfigResolver keycloakConfigResolver() {
return new KeycloakSpringBootConfigResolver();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
http
.cors()
.and()
.csrf()
.disable()
.authorizeRequests()
.antMatchers(PERMIT_URL)
.permitAll()
.anyRequest().authenticated()
.and()
.exceptionHandling()
// 异常处理
.authenticationEntryPoint((request, response, authException) -> response.sendError(HttpServletResponse.SC_UNAUTHORIZED))
.and()
.httpBasic();
}
}
// 获取token可以有多种方式实现,keycloak已经内置了/oath/token接口获取token,
// 不过对已有系统兼容可能不太友好,会有重定向,跨域之类的问题
// 这里提供两种自定义获取token的方法,也为了可以兼容oauth2获取token
// 3.1、通过过滤器实现登录接口拦截,获取token
/**
* @Description: 登录接口拦截
* @Author: zengzhengfu
* @Date: 2021/4/13
*/
@Slf4j
@WebFilter(filterName = "LoginFilter", urlPatterns = "/oauth/token")
public class LoginFilter implements Filter {
private final IAuthService iAuthService;
@Override
public void destroy() {
}
public LoginFilter(IAuthService iAuthService) {
this.iAuthService = iAuthService;
}
@Override
public void doFilter(ServletRequest arg0, ServletResponse arg1, FilterChain arg2) throws IOException, ServletException {
String requestUri = ((HttpServletRequest) arg0).getRequestURI();
log.info("当前请求接口: " + requestUri);
StringBuilder sb = new StringBuilder();
InputStream inputStream = null;
BufferedReader bufferedReader = null;
if (!MediaType.APPLICATION_JSON_VALUE.equals(arg0.getContentType())) {
throw new ApplicationException("Request Headers ContentType错误: " + arg0.getContentType());
}
BodyReaderRequestWrapper wrapper = new BodyReaderRequestWrapper((HttpServletRequest) arg0);
try {
inputStream = wrapper.getInputStream();
bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
String line;
while ((line = bufferedReader.readLine()) != null) {
sb.append(line);
}
} catch (IOException e) {
log.error("", e);
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (bufferedReader != null) {
try {
bufferedReader.close();
} catch (IOException e) {
log.error("", e);
}
}
}
HashMap hashMap = JSON.parseObject(sb.toString(), HashMap.class);
// Map<String, String[]> parameterMap = arg0.getParameterMap();
String requestJson = sb.toString();
String username = hashMap.get("username").toString();
String passwords = hashMap.get("password").toString();
String grantTypes = hashMap.get("grant_type").toString();
String respJson = "";
OAuth2AccessToken token;
try {
token = iAuthService.getToken(grantTypes, username, passwords, null);
ResponseData success = success(token);
JSONObject jsonObject = JSON.parseObject(JSONObject.toJSONString(success));
JSONObject convert = JsonConvertUtil.Convert(jsonObject);
respJson = convert.toJSONString();
} catch (ResponseException e) {
ResponseData fail;
if (e.getStatus().equals(RespCodeEnum.USER_LOCKED.getMsgCode())) {
fail = ResponseData.fail(RespCodeEnum.USER_LOCKED);
} else if (e.getStatus().equals(RespCodeEnum.USER_OR_PASSWORD_ERROR.getMsgCode())) {
fail = ResponseData.fail(RespCodeEnum.USER_OR_PASSWORD_ERROR);
} else {
fail = ResponseData.fail();
}
respJson = JSONObject.toJSONString(fail);
}
ServletOutputStream out = arg1.getOutputStream();
out.write(respJson.getBytes());
out.flush();
}
@Override
public void init(FilterConfig arg0) throws ServletException {
}
}
/**
* 用户登录keycloak获取token
*/
public interface IAuthService {
/**
* 获取当前用户的accessToken
*
* @param grantType
* @param username
* @param password
* @param refreshToken
* @return
*/
OAuth2AccessToken getToken(String grantType, String username, String password, String refreshToken) throws ResponseException;
}
/**
* @Description: 用户登录keycloak获取token
*/
@Service
@Slf4j
@AllArgsConstructor
public class AuthServiceImpl implements IAuthService {
private OAuth2ClientConfiguration oAuth2ClientConfiguration;
ApplicationContext applicationContext;
WorkPropertise workPropertise;
@Resource
private RestTemplate restTemplate;
//用户名账号密码错误
public static final String ERROR_USERNAME_PASSWORD = "Error requesting access token.";
//用户被禁用
public static final String USER_DISABLE = "Access token denied.";
@Override
public OAuth2AccessToken getToken(String grantType, String username, String password, String refreshToken) throws ResponseException {
OAuth2AccessToken accessToken;
ResourceOwnerPasswordResourceDetails resource = new ResourceOwnerPasswordResourceDetails();
resource.setUsername(username);
resource.setPassword(password);
oAuth2ClientConfiguration.configResource(resource);
AccessTokenRequest accessTokenRequest = new DefaultAccessTokenRequest();
OAuth2ClientContext oAuth2ClientContext = new DefaultOAuth2ClientContext(accessTokenRequest);
OAuth2RestTemplate oAuth2RestTemplate = new OAuth2RestTemplate(resource, oAuth2ClientContext);
if (REFRESH_TOKEN.equals(grantType)) {
ResourceOwnerPasswordAccessTokenProvider resourceOwnerPasswordAccessTokenProvider = new ResourceOwnerPasswordAccessTokenProvider();
accessToken = resourceOwnerPasswordAccessTokenProvider.refreshAccessToken(resource,
new DefaultOAuth2RefreshToken(refreshToken), accessTokenRequest);
} else {
try {
SecurityContextHolder.getContext().setAuthentication(null);
accessToken = oAuth2RestTemplate.getAccessToken();
} catch (OAuth2AccessDeniedException e) {
log.error("登录认证异常: ", e);
// 判断异常信息,进行抛出
if (ERROR_USERNAME_PASSWORD.equals(e.getMessage())) {
// 用户名密码错误
throw new ResponseException(RespCodeEnum.USER_OR_PASSWORD_ERROR);
} else if (USER_DISABLE.equals(e.getMessage())) {
// 用户被禁用
throw new ResponseException(RespCodeEnum.USER_LOCKED);
} else {
throw new ResponseException(RespCodeEnum.KC_USER_LOGIN_EXCEPTION);
}
}
}
return accessToken;
}
}
//3.2 弃用keycloak内置的获取token的接口,自定义获取token接口
/**
* @description: keycloak接口
* @create: 2020-06-11 11:36
**/
@Slf4j
@RestController
@RequestMapping("/keycloak")
public class AuthKeycloakController {
@Autowired
AuthKeycloakService keycloakService;
@Autowired
IAuthService iAuthService;
@PostMapping("/oauth/token")
public ResponseData getToken(@RequestBody @Validated TokenDTO tokenDTO) {
OAuth2AccessToken token = iAuthService.getToken(tokenDTO.getGrant_type(), tokenDTO.getUsername(),
tokenDTO.getPassword(), tokenDTO.getClient_id(), tokenDTO.getClient_secret(), tokenDTO.getRefresh_token());
return ResponseData.success(token);
}
}
public interface IAuthService {
/**
* 获取Client的token
*
* @param grantType
* @param username
* @param password
* @param clientId
* @param clientSecret
* @param refreshToken
* @return
*/
OAuth2AccessToken getToken(String grantType, String username, String password, String clientId, String clientSecret, String refreshToken);
}
public class AuthServiceImpl implements IAuthService {
private OAuth2ClientConfiguration oAuth2ClientConfiguration;
ApplicationContext applicationContext;
WorkPropertise workPropertise;
@Resource
private RestTemplate restTemplate;
//用户名账号密码错误
public static final String ERROR_USERNAME_PASSWORD = "Error requesting access token.";
//用户被禁用
public static final String USER_DISABLE = "Access token denied.";
@Override
public OAuth2AccessToken getToken(String grantType, String username, String password, String clientId, String clientSecret, String refreshToken) {
BaseOAuth2ProtectedResourceDetails resource = null;
switch (grantType) {
case CLIENT_CREDENTIALS:
resource = new ClientCredentialsResourceDetails();
resource.setClientId(clientId);
resource.setClientSecret(clientSecret);
break;
case PASSWORD:
resource = new ResourceOwnerPasswordResourceDetails();
((ResourceOwnerPasswordResourceDetails) resource).setUsername(username);
((ResourceOwnerPasswordResourceDetails) resource).setPassword(password);
resource.setClientId(clientId);
resource.setClientSecret(clientSecret);
break;
case REFRESH_TOKEN:
break;
default:
throw new ResponseException(RespCodeEnum.AUTHORIZATION_TYPE_NOT_SUPPORT);
}
resource.setAccessTokenUri(workPropertise.getTokenUrl());
resource.setGrantType(grantType);
resource.setClientId(workPropertise.getKcClientId());
resource.setClientSecret(workPropertise.getKcClientSecret());
resource.setScope(getScopesList("email", "profile"));
OAuth2AccessToken accessToken;
AccessTokenRequest accessTokenRequest = new DefaultAccessTokenRequest();
OAuth2ClientContext oAuth2ClientContext = new DefaultOAuth2ClientContext(accessTokenRequest);
OAuth2RestTemplate oAuth2RestTemplate = new OAuth2RestTemplate(resource, oAuth2ClientContext);
if (REFRESH_TOKEN.equals(grantType)) {
ResourceOwnerPasswordAccessTokenProvider resourceOwnerPasswordAccessTokenProvider = new ResourceOwnerPasswordAccessTokenProvider();
accessToken = resourceOwnerPasswordAccessTokenProvider.refreshAccessToken(resource,
new DefaultOAuth2RefreshToken(refreshToken), accessTokenRequest);
} else {
SecurityContextHolder.getContext().setAuthentication(null);
try {
accessToken = oAuth2RestTemplate.getAccessToken();
} catch (Exception e) {
if (e instanceof OAuth2AccessDeniedException) {
// 账号密码错误 Access token denied.
throw new ResponseException(RespCodeEnum.PERMISSION_DENIED);
}
// 判断异常信息,进行抛出
if (ERROR_USERNAME_PASSWORD.equals(e.getMessage())) {
// 用户名密码错误
throw new ResponseException(RespCodeEnum.USER_OR_PASSWORD_ERROR);
} else if (USER_DISABLE.equals(e.getMessage())) {
// 用户被禁用
throw new ResponseException(RespCodeEnum.USER_LOCKED);
} else {
throw new ResponseException(RespCodeEnum.KC_USER_LOGIN_EXCEPTION);
}
}
}
return accessToken;
}
}
// 3.3 获取Master token
// 对于微服务间或不同平台间,系统对外接口的调用,希望既可以对接口做认证授权,又不希望通过接口的方式获取token,
// 我们可以直接通过配置文件拿到master域以超级管理员的方式获取token,此token拥有最高权限的token,仅限于内部调用 切勿暴露,代码实现:
/**
* 此token为Master realm admin-cli token 仅限于内部程序使用
* 默认过期时间为60sec 若程序执行时间超过60sec 可以设置keycloak:
* Master realm Admin-cli clients Advanced Settings Access Token Lifespan 10 min
*
* @return Master token
*/
public String getMasterToken() {
String token = null;
//${keycloak.auth-server-url}
String authServerUrl = workPropertise.getAuthServerUrl() + "realms/master/protocol/openid-connect/token";
MultiValueMap<String, String> map = new LinkedMultiValueMap<>();
map.add("client_id", workPropertise.getResource());
map.add("username", workPropertise.getAdminUserName());
map.add("password", workPropertise.getAdminPassword());
map.add("grant_type", "password");
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
HttpEntity<MultiValueMap<String, String>> multiValueMapHttpEntity = new HttpEntity<>(map, httpHeaders);
try {
ResponseEntity<String> responseEntity = restTemplate.exchange(authServerUrl, HttpMethod.POST, multiValueMapHttpEntity, String.class);
JSONObject jsonObject = JSON.parseObject(responseEntity.getBody());
// 获取值
token = jsonObject.getString("access_token");
log.debug("调用keycloak API获取Master token成功: {}", jsonObject);
} catch (Exception e) {
log.error("调用keycloak API获取Master token失败: ", e);
}
return "Bearer " + token;
}
//4、keycloak用户、角色的增删改查,可以通过keycloak的java API实现,也可以通过keycloak提供的rest API实现
// 其实java API最后也是通过http的方式调用keycloak REST API实现的,个人感觉java API调用方便,rest API灵活
// 4.1、java API实现 这里仅以用户举例,角色、用户角色关系的调用类似
/**
* @Description: keyCloak客户端的初始化
* @Author: csn
* @Date: 2023/03/22 11:58
*/
@AllArgsConstructor
@Component
@Getter
@Setter
public class KeycloakAdminClient {
@Resource
private WorkPropertise workPropertise;
private Keycloak keycloak;
private RealmResource edge;
private RealmResource master;
/**
* 初始化keycloak的方法
*/
@PostConstruct
private void init() {
keycloak = Keycloak.getInstance(
// keycloak 服务地址 "${keycloak.auth-server-url}"
workPropertise.getAuthServerUrl(),
// keycloak 管理员的域 "${kc.target-realm}"
workPropertise.getTargetRealm(),
//你自己创建的管理员的账号 "${kc.master-realm-user-name}"
workPropertise.getAdminUserName(),
// 你自己创建的管理员的密码 "${kc.master-realm-user-password}"
workPropertise.getAdminPassword(),
// admin-cli 客户端 ${keycloak.resource}
workPropertise.getResource()
);
// 你创建的realm
edge = keycloak.realm(workPropertise.getRealm());
// master realm
master = keycloak.realm(workPropertise.getTargetRealm());
}
}
/**
* @author csn
* date 2021/5/7 15:22
* description
*/
@Slf4j
@AllArgsConstructor
@Service
public class AuthKeycloakServiceImpl implements AuthKeycloakService {
@Autowired
private KeycloakAdminClient keycloakAdminClient;
@Autowired
private WorkPropertise workPropertise;
@Override
public void addKcUser(UserT userT) {
RealmResource realmResource = keycloakAdminClient.getEdge();
Keycloak keycloak = keycloakAdminClient.getKeycloak();
UsersResource userResource = realmResource.users();
UserRepresentation newUser = new UserRepresentation();
// 构建用户信息用户——密码在创建完用户之后进行设置密码
newUser.setUsername(userT.getUsername());
newUser.setFirstName(userT.getName());
// 创建用户
Response createUserResponse = keycloak.realm(workPropertise.getRealm()).users().create(newUser);
// 判断创建用户状态
Response.StatusType createUserStatus = createUserResponse.getStatusInfo();
// 返回请求的地址
URI location = createUserResponse.getLocation();
String userId;
if (Response.Status.CREATED.getStatusCode() == createUserStatus.getStatusCode()) {
log.info("keycloak client 创建用户成功,创建用户的URI:{}", location);
// 获取用户id
userId = createUserResponse.getLocation().getPath().replaceAll(".*/([^/]+)$", "$1");
// 设置密码
CredentialRepresentation passwordCred = new CredentialRepresentation();
passwordCred.setTemporary(false);
passwordCred.setType(CredentialRepresentation.PASSWORD);
passwordCred.setValue(userT.getPassword());
userResource.get(userId).resetPassword(passwordCred);
log.info("keycloak client 同步创建{}用户成功", userT.getUsername());
String salt = passwordCred.getSalt();
String secretData = passwordCred.getSecretData();
} else if (Response.Status.CONFLICT.getStatusCode() == createUserStatus.getStatusCode()) {
log.info("keycloak client 同步获取{}用户成功", userT.getUsername());
} else {
log.error("新增{}用户异常", userT.getUsername());
}
}
@Override
public void updateKcUser(UserT userT) {
Keycloak keycloak = keycloakAdminClient.getKeycloak();
RealmResource realmResource = keycloakAdminClient.getEdge();
UsersResource userResource = realmResource.users();
// 获取keycloak指定用户
List<UserRepresentation> search = keycloak.realm(workPropertise.getRealm()).users().search(userT.getUsername());
if (search.size() == 1) {
UserRepresentation representation = search.get(0);
representation.setEnabled(EnabledEnum.getEnabledByCode(userT.getState()));
UserRepresentation newUser = new UserRepresentation();
newUser.setEnabled(EnabledEnum.getEnabledByCode(userT.getState()));
newUser.setFirstName(userT.getName());
userResource.get(representation.getId()).update(newUser);
}
}
@Override
public void resetPasswordKcUser(UserT userT, String newPassword) {
try {
RealmResource edge = keycloakAdminClient.getEdge();
UsersResource users = edge.users();
UserResource userResource = users.get(userT.getUsername());
CredentialRepresentation credentialRepresentation = new CredentialRepresentation();
credentialRepresentation.setTemporary(false);
credentialRepresentation.setType(CredentialRepresentation.PASSWORD);
credentialRepresentation.setValue(newPassword);
// 重置用户密码
userResource.resetPassword(credentialRepresentation);
} catch (Exception e) {
log.error("keyclaok客户端重置密码异常:", e);
throw new ResponseException(RespCodeEnum.KC_USER_RESSET_EXCEPTION);
}
}
}
//4.2 REST API实现 这里以绑定用户-角色关系举例(虽然我们不使用keycloak的用户角色映射,但是想要使用的小伙伴可以参考)
// rest API 无非就是组装请求url,请求头,请求体,然后用restTemplate去发http请求
public void syncUserRoleMapping(SysUserEntity sysUserEntity) {
String masterToken = iAuthService.getMasterToken();
Keycloak keycloak = keycloakAdminClient.getKeycloak();
// 构建keycloak域角色映射url
String url = workPropertise.getAuthServerUrl() + "admin/realms/" + workPropertise.getRealm() + "/users/" + sysUserEntity.getKeycloakId() + "/role-mappings/realm";
// 获取用户角色名称列表
List<String> roleNames = sysRoleMapper.selectUserRoleByUserId(sysUserEntity.getId());
ArrayList<RoleRepresentation> roleMappingList = new ArrayList<>();
for (String roleName : roleNames) {
// 获取域角色by name
List<RoleRepresentation> list = keycloak.realm(workPropertise.getRealm()).roles().list(roleName, true);
roleMappingList.addAll(list);
}
String roleMappingJson = JSON.toJSONString(roleMappingList);
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.add(HttpHeaders.AUTHORIZATION, masterToken);
httpHeaders.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<String> request = new HttpEntity<>(roleMappingJson, httpHeaders);
HttpHeaders getHeader = new HttpHeaders();
getHeader.add(HttpHeaders.AUTHORIZATION, masterToken);
HttpEntity<MultiValueMap<String, Object>> httpEntity = new HttpEntity<>(getHeader);
try {
// *清除旧角色映射
ResponseEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.GET, httpEntity, String.class);
JSONArray jsonArray = JSONArray.parseArray(responseEntity.getBody());
if (jsonArray != null) {
List<RoleRepresentation> deleteRoles = jsonArray.toJavaList(RoleRepresentation.class);
String deleteMapping = JSON.toJSONString(deleteRoles);
HttpEntity<String> deleteRequest = new HttpEntity<>(deleteMapping, httpHeaders);
restTemplate.exchange(url, HttpMethod.DELETE, deleteRequest, String.class);
}
//创建新角色映射
ResponseEntity<String> addResponse = restTemplate.exchange(url, HttpMethod.POST, request, String.class);
log.debug("调用keycloak API成功,返回信息:{}", addResponse);
} catch (Exception e) {
log.error("调用keycloak API同步域角色映射失败,失败信息为:", e);
}
}
// 为什么要用用户-角色关系举例,因为我没有找到keycloak用户角色映射javaAPI,好像没有~ 所以用户角色映射暂时只好通过这种方式实现
// 希望找到相关javaAPI的小伙伴评论指出
// keycloak的rest API地址:https://www.keycloak.org/docs-api/20.0.3/rest-api/index.html
//5 对于已登录的用户 我们希望系统直接获取当前登录用户的用户名,可以通过以下方式实现,可以写在common包里 作为工具类给其他微服务使用
/**
* 获取当前登录用户名称
*
* @return username
*/
public static String getUsername() {
String username;
try {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
username = authentication.getName();
} catch (Exception e) {
throw new UsernameNotFoundException("获取当前登录用户失败" + e);
}
return username;
}
// 6 其他微服务引入keycloak 做接口认证授权 仅需两步
// 6.1、pom文件引入common的pom 6.2、yml配置文件加上以下keycloak配置
keycloak:
auth-server-url: http://127.0.0.1:8411/auth/
resource: admin-cli
realm: test
principal-attribute: preferred_username
use-resource-role-mappings: false
ssl-required: none
// 6.3、对应的微服务加以下配置
@KeycloakConfiguration
@RequiredArgsConstructor
public class SecurityConfig extends KeycloakWebSecurityConfigurerAdapter {
// 接口放开
String[] PERMIT_URL = {"/user/info","/user/login/info","/xxx"};
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) {
KeycloakAuthenticationProvider keycloakAuthenticationProvider = keycloakAuthenticationProvider();
SimpleAuthorityMapper grantedAuthorityMapper = new SimpleAuthorityMapper();
grantedAuthorityMapper.setPrefix("ROLE_");
keycloakAuthenticationProvider.setGrantedAuthoritiesMapper(grantedAuthorityMapper);
auth.authenticationProvider(keycloakAuthenticationProvider);
}
@Bean
@Override
protected SessionAuthenticationStrategy sessionAuthenticationStrategy() {
return new RegisterSessionAuthenticationStrategy(new SessionRegistryImpl());
}
@Bean
public org.keycloak.adapters.KeycloakConfigResolver keycloakConfigResolver() {
return new KeycloakSpringBootConfigResolver();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
super.configure(http);
http
.cors()
.and()
.csrf()
.disable()
.authorizeRequests()
// .antMatchers()
// 接口放开
.antMatchers(PERMIT_URL)
.permitAll()
.anyRequest().authenticated()
.and()
.exceptionHandling()
// 异常处理
.authenticationEntryPoint((request, response, authException) -> response.sendError(HttpServletResponse.SC_UNAUTHORIZED))
.and()
.httpBasic();
}
}
参考 keycloak授权服务指南:
https://www.keycloak.org/docs/latest/authorization_services/
附:keycloak客户端配置文件:test.json