SSO :同一个帐号在同一个公司不同系统上登陆
使用SpringSecurity实现类似于SSO登陆系统是十分简单的 下面我就搭建一个DEMO
首先来看看目录的结构
其中sso-demo是父工程项目 sso-client 、sso-client2分别对应2个资源服务器,sso-server是认证服务器
引入的pom文件
sso-demo
<&#63;xml version="1.0" encoding="UTF-8"&#63;>4.0.0 study.security.sso sso-demo 1.0.0-SNAPSHOT sso-server sso-client sso-client2 pom io.spring.platform platform-bom Brussels-SR4 pom import org.springframework.cloud spring-cloud-dependencies Dalston.SR2 pom import org.apache.maven.plugins maven-compiler-plugin 2.3.2 1.8 UTF-8
sso-server
<&#63;xml version="1.0" encoding="UTF-8"&#63;>sso-demo study.security.sso 1.0.0-SNAPSHOT 4.0.0 sso-server org.springframework.boot spring-boot-starter-security org.springframework.boot spring-boot-starter-web org.springframework.security.oauth spring-security-oauth2 org.springframework.security spring-security-jwt
sso-client与sso-client2 pom 中的 是一样的
1.sso-server
现在开始搭建认证服务器
认证服务器的目录结构如下
/** * 认证服务器配置 * Created by ZhuPengWei on 2018/1/11. */ @Configuration @EnableAuthorizationServer public class SsoAuthenticationServerConfig extends AuthorizationServerConfigurerAdapter { @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients.inMemory() .withClient("client1") .secret("client1") .authorizedGrantTypes("authorization_code", "refresh_token") .scopes("all") .and() .withClient("client2") .secret("client2") .authorizedGrantTypes("authorization_code", "refresh_token") .scopes("all"); } @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { endpoints.tokenStore(jwtTokenStore()).accessTokenConverter(jwtAccessTokenConverter()); } /** * 认证服务器的安全配置 * * @param security * @throws Exception */ @Override public void configure(AuthorizationServerSecurityConfigurer security) throws Exception { // 要访问认证服务器tokenKey的时候需要经过身份认证 security.tokenKeyAccess("isAuthenticated()"); } @Bean public TokenStore jwtTokenStore() { return new JwtTokenStore(jwtAccessTokenConverter()); } @Bean public JwtAccessTokenConverter jwtAccessTokenConverter() { JwtAccessTokenConverter jwtAccessTokenCOnverter= new JwtAccessTokenConverter(); // 保证JWT安全的唯一方式 jwtAccessTokenConverter.setSigningKey("ZPW"); return jwtAccessTokenConverter; } }
/** * 自定义用户登陆逻辑配置 * Created by ZhuPengWei on 2018/1/13. */ @Configuration public class SsoSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private UserDetailsService userDetailsService; /** * 加密解密逻辑 */ @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } @Override protected void configure(HttpSecurity http) throws Exception { // 改成表单登陆的方式 所有请求都需要认证 http.formLogin().and().authorizeRequests().anyRequest().authenticated(); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { // 用自己的登陆逻辑以及加密器 auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder()); } }
/** * 自定义用户登陆 * Created by ZhuPengWei on 2018/1/13. */ @Component public class SsoUserDetailsService implements UserDetailsService { @Autowired private PasswordEncoder passwordEncoder; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { return new User(username, passwordEncoder.encode("123456"), AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_USER")); } }
其中SsoApprovalEndPoint与SsoSpelView目的是去掉登陆之后授权的效果
注解 @FrameworkEndpoint
与@RestController注解相类似
如果声明和@FrameworkEndpoint一模一样的@RequestMapping
Spring框架处理的时候会优先处理@RestController里面的
/** * 自定义认证逻辑 * Created by ZhuPengWei on 2018/1/13. */ @RestController @SessionAttributes("authorizationRequest") public class SsoApprovalEndpoint { @RequestMapping("/oauth/confirm_access") public ModelAndView getAccessConfirmation(Mapmodel, HttpServletRequest request) throws Exception { String template = createTemplate(model, request); if (request.getAttribute("_csrf") != null) { model.put("_csrf", request.getAttribute("_csrf")); } return new ModelAndView(new SsoSpelView(template), model); } protected String createTemplate(Map model, HttpServletRequest request) { String template = TEMPLATE; if (model.containsKey("scopes") || request.getAttribute("scopes") != null) { template = template.replace("%scopes%", createScopes(model, request)).replace("%denial%", ""); } else { template = template.replace("%scopes%", "").replace("%denial%", DENIAL); } if (model.containsKey("_csrf") || request.getAttribute("_csrf") != null) { template = template.replace("%csrf%", CSRF); } else { template = template.replace("%csrf%", ""); } return template; } private CharSequence createScopes(Map model, HttpServletRequest request) { StringBuilder builder = new StringBuilder(" "); @SuppressWarnings("unchecked") Map
"); return builder.toString(); } private static String CSRF = ""; private static String DENIAL = "%csrf%"; // 对源代码进行处理 隐藏授权页面,并且使他自动提交 private static String TEMPLATE = "scopes = (Map ) (model.containsKey("scopes") &#63; model.get("scopes") : request .getAttribute("scopes")); for (String scope : scopes.keySet()) { String approved = "true".equals(scopes.get(scope)) &#63; " checked" : ""; String denied = !"true".equals(scopes.get(scope)) &#63; " checked" : ""; String value = SCOPE.replace("%scope%", scope).replace("%key%", scope).replace("%approved%", approved) .replace("%denied%", denied); builder.append(value); } builder.append(" "; private static String SCOPE = "OAuth Approval
" + "Do you authorize '${authorizationRequest.clientId}' to access your protected resources&#63;
" + "%csrf%%scopes%" + "%denial%"; } %scope%: Approve Deny
SsoSpelView 与 原来SpelView 是一样的 只不过原来SpelView 不是public的类
application.properties
server.port=9999 server.context-path=/server
2.sso-client
相对于认证服务器 资源服务器demo的配置就十分简单了
/** * Created by ZhuPengWei on 2018/1/11. */ @SpringBootApplication @RestController @EnableOAuth2Sso public class SsoClient1Application { @GetMapping("/user") public Authentication user(Authentication user) { return user; } public static void main(String[] args) { SpringApplication.run(SsoClient1Application.class, args); } }
SSO Demo Client1
访问client2
application.properties
security.oauth2.client.client-id=client1 security.oauth2.client.client-secret=client1 #需要认证时候跳转的地址 security.oauth2.client.user-authorization-uri=http://127.0.0.1:9999/server/oauth/authorize #请求令牌地址 security.oauth2.client.access-token-uri=http://127.0.0.1:9999/server/oauth/token #解析 security.oauth2.resource.jwt.key-uri=http://127.0.0.1:9999/server/oauth/token_key #sso server.port=8080 server.context-path=/client1
3.sso-client2
资源服务器1和资源服务器2的目录结构是一样的,改了相关的参数
/** * Created by ZhuPengWei on 2018/1/11. */ @SpringBootApplication @RestController @EnableOAuth2Sso public class SsoClient2Application { @GetMapping("/user") public Authentication user(Authentication user) { return user; } public static void main(String[] args) { SpringApplication.run(SsoClient2Application.class, args); } }
SSO Demo Client2
访问client1
security.oauth2.client.client-id=client2 security.oauth2.client.client-secret=client2 #需要认证时候跳转的地址 security.oauth2.client.user-authorization-uri=http://127.0.0.1:9999/server/oauth/authorize #请求令牌地址 security.oauth2.client.access-token-uri=http://127.0.0.1:9999/server/oauth/token #解析 security.oauth2.resource.jwt.key-uri=http://127.0.0.1:9999/server/oauth/token_key #sso server.port=8060 server.context-path=/client2
好了 基于JWT实现SSO单点登录的DEMO以及搭建完成了 下面来看看页面的效果
在初次访问的时候
图1
登陆成功之后
图2
图3
注意
写SsoApprovalEndPoint与SsoSpelView目的是去掉登陆之后授权的效果如果不写这2个类
在初次访问的登陆成功之后是有一步授权的操作的
比如说图1操作成功之后
点击Authorize才会跳转到图2
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。