// 项目架构 微服务划分: // 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
标签:resource,String,跨平台,token,new,keycloak,public,SpringBoot From: https://www.cnblogs.com/axibug/p/17277182.html