2018年7月14日 | Leave a comment https://www.jianshu.com/p/efd32ace20dc 前言 登陆是一个项目的基础,几乎任何项目都需要包括登陆模块,网上大部分登陆都是使用的shrio,个人感觉这种东西很老,而且不好用,偶然之前发现了一个叫keycloak的sso开源项目, 感觉挺不错的,这篇文章主要讲解如何使用springboot和keycloak进行结合。 版本 springboot: 1.4.3.RELEASE 版本 keycloak: 2.5.1.Final 版本 vue: 2.1.0 版本 项目搭建 首先搭建keycloak的服务 (略) 编写前端代码(用vue2写的简单的一个spa,略) 编写springboot的服务(略) 加入核心依赖 <span class="hljs-tag"><<span class="hljs-name">dependency</span>></span> <span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>org.keycloak<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span> <span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>keycloak-tomcat8-adapter<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span> <span class="hljs-tag"></<span class="hljs-name">dependency</span>></span> <span class="hljs-tag"><<span class="hljs-name">dependency</span>></span> <span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>org.keycloak<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span> <span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>keycloak-spring-security-adapter<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span> <span class="hljs-tag"></<span class="hljs-name">dependency</span>></span> 12345678910 <span class="hljs-tag"><<span class="hljs-name">dependency</span>></span> <span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>org.keycloak<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span> <span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>keycloak-tomcat8-adapter<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span> <span class="hljs-tag"></<span class="hljs-name">dependency</span>></span> <span class="hljs-tag"><<span class="hljs-name">dependency</span>></span> <span class="hljs-tag"><<span class="hljs-name">groupId</span>></span>org.keycloak<span class="hljs-tag"></<span class="hljs-name">groupId</span>></span> <span class="hljs-tag"><<span class="hljs-name">artifactId</span>></span>keycloak-spring-security-adapter<span class="hljs-tag"></<span class="hljs-name">artifactId</span>></span> <span class="hljs-tag"></<span class="hljs-name">dependency</span>></span> 在springboot的配置文件中加入 keycloak: configurationFile: <span class="hljs-string">"classpath:keycloak.json"</span> 123 keycloak: configurationFile: <span class="hljs-string">"classpath:keycloak.json"</span> 在resources的目录下加入 keycloak.json配置文件 { <span class="hljs-attr">"realm"</span>: <span class="hljs-string">"family"</span>, <span class="hljs-attr">"bearer-only"</span>: <span class="hljs-literal">true</span>, <span class="hljs-attr">"auth-server-url"</span>: <span class="hljs-string">"http://localhost:8080/auth"</span>, <span class="hljs-attr">"ssl-required"</span>: <span class="hljs-string">"external"</span>, <span class="hljs-attr">"resource"</span>: <span class="hljs-string">"family-app"</span>, <span class="hljs-attr">"enable-cors"</span>: <span class="hljs-literal">true</span> } 123456789 { <span class="hljs-attr">"realm"</span>: <span class="hljs-string">"family"</span>, <span class="hljs-attr">"bearer-only"</span>: <span class="hljs-literal">true</span>, <span class="hljs-attr">"auth-server-url"</span>: <span class="hljs-string">"http://localhost:8080/auth"</span>, <span class="hljs-attr">"ssl-required"</span>: <span class="hljs-string">"external"</span>, <span class="hljs-attr">"resource"</span>: <span class="hljs-string">"family-app"</span>, <span class="hljs-attr">"enable-cors"</span>: <span class="hljs-literal">true</span>} 配置springmvc的跨域filter <span class="hljs-meta">@Configuration</span> <span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">CorsFilterConfig</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Filter</span> </span>{ <span class="hljs-meta">@Override</span> <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">init</span><span class="hljs-params">(FilterConfig filterConfig)</span> <span class="hljs-keyword">throws</span> ServletException </span>{} <span class="hljs-meta">@Override</span> <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">doFilter</span><span class="hljs-params">(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)</span> <span class="hljs-keyword">throws</span> IOException, ServletException </span>{ HttpServletResponse res = (HttpServletResponse) servletResponse; res.setHeader(<span class="hljs-string">"Access-Control-Allow-Origin"</span>, <span class="hljs-string">"*"</span>); res.setHeader(<span class="hljs-string">"Access-Control-Allow-Methods"</span>, <span class="hljs-string">"POST, GET, OPTIONS, DELETE, PUT"</span>); res.setHeader(<span class="hljs-string">"Access-Control-Max-Age"</span>, <span class="hljs-string">"1728000"</span>); res.setHeader(<span class="hljs-string">"Access-Control-Allow-Headers"</span>, <span class="hljs-string">"Authorization, Content-Type, Accept, x-requested-with, Cache-Control"</span>); filterChain.doFilter(servletRequest, res); } <span class="hljs-meta">@Override</span> <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">destroy</span><span class="hljs-params">()</span> </span>{} } 12345678910111213141516171819202122 <span class="hljs-meta">@Configuration</span><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">CorsFilterConfig</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Filter</span> </span>{ <span class="hljs-meta">@Override</span> <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">init</span><span class="hljs-params">(FilterConfig filterConfig)</span> <span class="hljs-keyword">throws</span> ServletException </span>{} <span class="hljs-meta">@Override</span> <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">doFilter</span><span class="hljs-params">(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)</span> <span class="hljs-keyword">throws</span> IOException, ServletException </span>{ HttpServletResponse res = (HttpServletResponse) servletResponse; res.setHeader(<span class="hljs-string">"Access-Control-Allow-Origin"</span>, <span class="hljs-string">"*"</span>); res.setHeader(<span class="hljs-string">"Access-Control-Allow-Methods"</span>, <span class="hljs-string">"POST, GET, OPTIONS, DELETE, PUT"</span>); res.setHeader(<span class="hljs-string">"Access-Control-Max-Age"</span>, <span class="hljs-string">"1728000"</span>); res.setHeader(<span class="hljs-string">"Access-Control-Allow-Headers"</span>, <span class="hljs-string">"Authorization, Content-Type, Accept, x-requested-with, Cache-Control"</span>); filterChain.doFilter(servletRequest, res); } <span class="hljs-meta">@Override</span> <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">destroy</span><span class="hljs-params">()</span> </span>{}} 配置keycloak和spring security结合的配置文件 <span class="hljs-meta">@Configuration</span> <span class="hljs-meta">@EnableWebSecurity</span> <span class="hljs-meta">@EnableGlobalMethodSecurity</span>(prePostEnabled = <span class="hljs-keyword">true</span>) <span class="hljs-meta">@ComponentScan</span>(basePackageClasses = KeycloakSecurityComponents.class) <span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">KeycloakSecurityConfig</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">KeycloakWebSecurityConfigurerAdapter</span> </span>{ <span class="hljs-meta">@Autowired</span> <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">configureGlobal</span><span class="hljs-params">(AuthenticationManagerBuilder auth)</span> <span class="hljs-keyword">throws</span> Exception </span>{ auth.authenticationProvider(keycloakAuthenticationProvider()); } <span class="hljs-meta">@Bean</span> <span class="hljs-meta">@Override</span> <span class="hljs-function"><span class="hljs-keyword">protected</span> SessionAuthenticationStrategy <span class="hljs-title">sessionAuthenticationStrategy</span><span class="hljs-params">()</span> </span>{ <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> NullAuthenticatedSessionStrategy(); } <span class="hljs-meta">@Bean</span> <span class="hljs-function"><span class="hljs-keyword">public</span> FilterRegistrationBean <span class="hljs-title">keycloakAuthenticationProcessingFilterRegistrationBean</span><span class="hljs-params">( KeycloakAuthenticationProcessingFilter filter)</span> </span>{ FilterRegistrationBean registrationBean = <span class="hljs-keyword">new</span> FilterRegistrationBean(filter); registrationBean.setEnabled(<span class="hljs-keyword">false</span>); <span class="hljs-keyword">return</span> registrationBean; } <span class="hljs-meta">@Bean</span> <span class="hljs-function"><span class="hljs-keyword">public</span> FilterRegistrationBean <span class="hljs-title">keycloakPreAuthActionsFilterRegistrationBean</span><span class="hljs-params">( KeycloakPreAuthActionsFilter filter)</span> </span>{ FilterRegistrationBean registrationBean = <span class="hljs-keyword">new</span> FilterRegistrationBean(filter); registrationBean.setEnabled(<span class="hljs-keyword">false</span>); <span class="hljs-keyword">return</span> registrationBean; } <span class="hljs-meta">@Override</span> <span class="hljs-function"><span class="hljs-keyword">protected</span> <span class="hljs-keyword">void</span> <span class="hljs-title">configure</span><span class="hljs-params">(HttpSecurity http)</span> <span class="hljs-keyword">throws</span> Exception </span>{ <span class="hljs-keyword">super</span>.configure(http); http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) .sessionAuthenticationStrategy(sessionAuthenticationStrategy()).and() .addFilterBefore(keycloakPreAuthActionsFilter(), LogoutFilter.class) .addFilterBefore(keycloakAuthenticationProcessingFilter(), X509AuthenticationFilter.class) .exceptionHandling().authenticationEntryPoint(authenticationEntryPoint()).and() .authorizeRequests() .requestMatchers(CorsUtils::isCorsRequest).permitAll() <span class="hljs-comment">// .antMatchers("/family/*").hasAnyAuthority("user").antMatchers("/admin/*").hasRole("ADMIN")</span> .antMatchers(<span class="hljs-string">"/**"</span>).authenticated() .anyRequest().permitAll(); } } 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849 <span class="hljs-meta">@Configuration</span><span class="hljs-meta">@EnableWebSecurity</span><span class="hljs-meta">@EnableGlobalMethodSecurity</span>(prePostEnabled = <span class="hljs-keyword">true</span>)<span class="hljs-meta">@ComponentScan</span>(basePackageClasses = KeycloakSecurityComponents.class)<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">KeycloakSecurityConfig</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">KeycloakWebSecurityConfigurerAdapter</span> </span>{ <span class="hljs-meta">@Autowired</span> <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">configureGlobal</span><span class="hljs-params">(AuthenticationManagerBuilder auth)</span> <span class="hljs-keyword">throws</span> Exception </span>{ auth.authenticationProvider(keycloakAuthenticationProvider()); } <span class="hljs-meta">@Bean</span> <span class="hljs-meta">@Override</span> <span class="hljs-function"><span class="hljs-keyword">protected</span> SessionAuthenticationStrategy <span class="hljs-title">sessionAuthenticationStrategy</span><span class="hljs-params">()</span> </span>{ <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> NullAuthenticatedSessionStrategy(); } <span class="hljs-meta">@Bean</span> <span class="hljs-function"><span class="hljs-keyword">public</span> FilterRegistrationBean <span class="hljs-title">keycloakAuthenticationProcessingFilterRegistrationBean</span><span class="hljs-params">( KeycloakAuthenticationProcessingFilter filter)</span> </span>{ FilterRegistrationBean registrationBean = <span class="hljs-keyword">new</span> FilterRegistrationBean(filter); registrationBean.setEnabled(<span class="hljs-keyword">false</span>); <span class="hljs-keyword">return</span> registrationBean; } <span class="hljs-meta">@Bean</span> <span class="hljs-function"><span class="hljs-keyword">public</span> FilterRegistrationBean <span class="hljs-title">keycloakPreAuthActionsFilterRegistrationBean</span><span class="hljs-params">( KeycloakPreAuthActionsFilter filter)</span> </span>{ FilterRegistrationBean registrationBean = <span class="hljs-keyword">new</span> FilterRegistrationBean(filter); registrationBean.setEnabled(<span class="hljs-keyword">false</span>); <span class="hljs-keyword">return</span> registrationBean; } <span class="hljs-meta">@Override</span> <span class="hljs-function"><span class="hljs-keyword">protected</span> <span class="hljs-keyword">void</span> <span class="hljs-title">configure</span><span class="hljs-params">(HttpSecurity http)</span> <span class="hljs-keyword">throws</span> Exception </span>{ <span class="hljs-keyword">super</span>.configure(http); http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) .sessionAuthenticationStrategy(sessionAuthenticationStrategy()).and() .addFilterBefore(keycloakPreAuthActionsFilter(), LogoutFilter.class) .addFilterBefore(keycloakAuthenticationProcessingFilter(), X509AuthenticationFilter.class) .exceptionHandling().authenticationEntryPoint(authenticationEntryPoint()).and() .authorizeRequests() .requestMatchers(CorsUtils::isCorsRequest).permitAll()<span class="hljs-comment">// .antMatchers("/family/*").hasAnyAuthority("user").antMatchers("/admin/*").hasRole("ADMIN")</span> .antMatchers(<span class="hljs-string">"/**"</span>).authenticated() .anyRequest().permitAll(); }} 对应的接口上增加可以执行的用户角色 <span class="hljs-meta">@RequestMapping</span>(value = <span class="hljs-string">"/all"</span>, method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE) <span class="hljs-meta">@ResponseBody</span> <span class="hljs-meta">@PreAuthorize</span>(<span class="hljs-string">"hasAnyAuthority('user')"</span>) <span class="hljs-function"><span class="hljs-keyword">public</span> List<Family> <span class="hljs-title">queryAllFamilyInfo</span><span class="hljs-params">()</span> </span>{ <span class="hljs-keyword">return</span> familyInfoService.queryAllFamilyInfo(); } 1234567 <span class="hljs-meta">@RequestMapping</span>(value = <span class="hljs-string">"/all"</span>, method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE) <span class="hljs-meta">@ResponseBody</span> <span class="hljs-meta">@PreAuthorize</span>(<span class="hljs-string">"hasAnyAuthority('user')"</span>) <span class="hljs-function"><span class="hljs-keyword">public</span> List<Family> <span class="hljs-title">queryAllFamilyInfo</span><span class="hljs-params">()</span> </span>{ <span class="hljs-keyword">return</span> familyInfoService.queryAllFamilyInfo(); } 遇到的坑 对应keycloak的用户权限需要使用 hasAnyAuthority而不是 hasAnyRole,并且需要在KeycloakSecurityConfig类中加入@EnableGlobalMethodSecurity(prePostEnabled = true)注解 keycloak-spring-security-adapter和 spring-boot-adpater两个依赖不能同时使用 cors的跨域的配置,需要CorsFilterConfig的filter类配合KeycloakSecurityConfig类中的 requestMatchers(CorsUtils::isCorsRequest).permitAll()一起使用 完整的项目地址(包括了简单的前端和后端代码,不会写前端,前端代码比较垃圾。。 需要完善 ): https://github.com/dragontree101/springboot-keycloak-demo 作者:LOC_Thomas 链接:https://www.jianshu.com/p/efd32ace20dc 來源:简书 简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。