å¾çæ¥èª Pexels
ç½ä¸å ³äºå®ç° SSO çæç« ä¸å¤§å ï¼ä½æ¯å½ä½ ççç §çåçæ¶åå°±ä¼åç°æ ¹æ¬ä¸æ¯é£ä¹åäºå¿ï¼ç®ç´è®©äººæçï¼å°¤å ¶æ¯å¯¹äºæè¿æ ·çèé¸ã
å ç»æ²æï¼ç»äºæå®äºï¼å³å®è®°å½ä¸æ¥ï¼ä»¥ä¾¿åç»æ¥çãå æ¥çä¸ä¸ææï¼
åå¤
â åç¹ç»å½
æå¸¸è§çä¾åæ¯ï¼æä»¬æå¼æ·å® APPï¼é¦é¡µå°±ä¼æå¤©ç«ãèåç®çæå¡ç龿¥ï¼å½ä½ ç¹å»ä»¥åå°±ç´æ¥è·³è¿å»äºï¼å¹¶æ²¡æè®©ä½ åç»å½ä¸æ¬¡ã
ä¸é¢è¿ä¸ªå¾æ¯æå¨ç½ä¸æ¾çï¼æè§å¾ç»å¾æ¯è¾æç½ï¼
坿æç¹å¿ä¸æ¸ æ°ï¼äºæ¯æåç»äºä¸ªç®ççï¼
éè¦çæ¯çè§£ï¼
SSO æå¡ç«¯å SSO 客æ·ç«¯ç´æ¥æ¯éè¿ææä»¥ååæ¾ Token ç形弿¥è®¿é®åä¿æ¤çèµæºã
ç¸å¯¹äºæµè§å¨æ¥è¯´ï¼ä¸å¡ç³»ç»æ¯æå¡ç«¯ï¼ç¸å¯¹äº SSO æå¡ç«¯æ¥è¯´ï¼ä¸å¡ç³»ç»æ¯å®¢æ·ç«¯ã
æµè§å¨åä¸å¡ç³»ç»ä¹é´éè¿ä¼è¯æ£å¸¸è®¿é®ã
䏿¯æ¯æ¬¡æµè§å¨è¯·æ±é½è¦å» SSO æå¡ç«¯å»éªè¯ï¼åªè¦æµè§å¨åå®æè®¿é®çæå¡ç«¯çä¼è¯ææå®å°±å¯ä»¥æ£å¸¸è®¿é®ã
å©ç¨ OAuth2 å®ç°åç¹ç»å½
æ¥ä¸æ¥ï¼åªè®²è·æ¬ä¾ç¸å ³çä¸äºé ç½®ï¼ä¸è®²åçï¼ä¸è®²ä¸ºä»ä¹ã
伿å¨ç¥ï¼å¨ OAuth2 å¨ææææå¡å¨ãèµæºæå¡å¨ã客æ·ç«¯è¿æ ·å 个è§è²ï¼å½æä»¬ç¨å®æ¥å®ç° SSO çæ¶åæ¯ä¸éè¦èµæºæå¡å¨è¿ä¸ªè§è²çï¼ææææå¡å¨å客æ·ç«¯å°±å¤äºã
æææå¡å¨å½ç¶æ¯ç¨æ¥å认è¯çï¼å®¢æ·ç«¯å°±æ¯å个åºç¨ç³»ç»ï¼æä»¬åªéè¦ç»å½æååæ¿å°ç¨æ·ä¿¡æ¯ä»¥åç¨æ·ææ¥æçæéå³å¯ã
ä¹åæä¸ç´è®¤ä¸ºæé£äºéè¦æéæ§å¶çèµæºæ¾å°èµæºæå¡å¨éä¿æ¤èµ·æ¥å°±å¯ä»¥å®ç°æéæ§å¶ï¼å ¶å®æ¯ææ³éäºï¼æéæ§å¶è¿å¾éè¿ Spring Security æè èªå®ä¹æ¦æªå¨æ¥åã
â Spring Security ãOAuth2ãJWTãSSO
卿¬ä¾ä¸ï¼ä¸å®è¦åæ¸ æ¥è¿å 个çä½ç¨ï¼
é¦å ï¼SSO æ¯ä¸ç§ææ³ï¼æè 说æ¯ä¸ç§è§£å³æ¹æ¡ï¼æ¯æ½è±¡çï¼æä»¬è¦åçå°±æ¯æç §å®çè¿ç§ææ³å»å®ç°å®ã
å ¶æ¬¡ï¼OAuth2 æ¯ç¨æ¥å è®¸ç¨æ·ææç¬¬ä¸æ¹åºç¨è®¿é®ä»å¨å¦ä¸ä¸ªæå¡å¨ä¸çèµæºçä¸ç§åè®®ï¼å®ä¸æ¯ç¨æ¥ååç¹ç»å½çï¼ä½æä»¬å¯ä»¥å©ç¨å®æ¥å®ç°åç¹ç»å½ã
卿¬ä¾å®ç° SSO çè¿ç¨ä¸ï¼åä¿æ¤çèµæºå°±æ¯ç¨æ·çä¿¡æ¯(å æ¬ï¼ç¨æ·çåºæ¬ä¿¡æ¯ï¼ä»¥åç¨æ·æå ·æçæé)ã
èæä»¬æ³è¦è®¿é®è¿è¿ä¸èµæºå°±éè¦ç¨æ·ç»å½å¹¶ææï¼OAuth2 æå¡ç«¯è´è´£ä»¤ççåæ¾çæä½ï¼è¿ä»¤çççææä»¬éç¨ JWTï¼ä¹å°±æ¯è¯´ JWT æ¯ç¨æ¥æ¿è½½ç¨æ·ç Access_Token çã
æåï¼Spring Security æ¯ç¨äºå®å ¨è®¿é®çï¼è¿éæä»¬æä»¬ç¨æ¥åè®¿é®æéæ§å¶ã
è®¤è¯æå¡å¨é ç½®
Maven ä¾èµï¼
4.0.0 org.springframework.boot spring-boot-starter-parent 2.1.3.RELEASE com.cjs.sso oauth2-sso-auth-server 0.0.1-SNAPSHOT oauth2-sso-auth-server 1.8 org.springframework.boot spring-boot-starter-data-jpa org.springframework.boot spring-boot-starter-data-redis org.springframework.boot spring-boot-starter-security org.springframework.security.oauth.boot spring-security-oauth2-autoconfigure 2.1.3.RELEASE org.springframework.boot spring-boot-starter-thymeleaf org.springframework.boot spring-boot-starter-web org.springframework.session spring-session-data-redis mysql mysql-connector-java runtime org.projectlombok lombok true org.springframework.boot spring-boot-starter-test test org.springframework.security spring-security-test test org.apache.commons commons-lang3 3.8.1 com.alibaba fastjson 1.2.56 org.springframework.boot spring-boot-maven-plugin
è¿é颿éè¦çä¾èµæ¯ï¼spring-security-oauth2-autoconfigureã
application.ymlï¼
spring: datasource: url: jdbc:mysql://localhost:3306/permission username: root password: 123456 driver-class-name: com.mysql.jdbc.Driver jpa: show-sql: true session: store-type: redis redis: host: 127.0.0.1 password: 123456 port: 6379 server: port: 8080
AuthorizationServerConfig(éè¦)ï¼
package com.cjs.sso.config; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import org.springframework.security.core.token.DefaultToken; import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer; import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter; import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer; import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer; import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer; import org.springframework.security.oauth2.provider.token.DefaultTokenServices; import org.springframework.security.oauth2.provider.token.TokenStore; import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter; import org.springframework.security.oauth2.provider.token.store.JwtTokenStore; import javax.sql.DataSource; /** * @author ChengJianSheng * @date 2019-02-11 */ @Configuration @EnableAuthorizationServer public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter { @Autowired private DataSource dataSource; @Override public void configure(AuthorizationServerSecurityConfigurer security) throws Exception { security.allowFormAuthenticationForClients(); security.tokenKeyAccess("isAuthenticated()"); } @Override public void configure(ClientDetailsServiceConfigurer clients) throws Exception { clients.jdbc(dataSource); } @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception { endpoints.accessTokenConverter(jwtAccessTokenConverter()); endpoints.tokenStore(jwtTokenStore()); // endpoints.tokenServices(defaultTokenServices()); } /*@Primary @Bean public DefaultTokenServices defaultTokenServices() { DefaultTokenServices defaultTokenServices = new DefaultTokenServices(); defaultTokenServices.setTokenStore(jwtTokenStore()); defaultTokenServices.setSupportRefreshToken(true); return defaultTokenServices; }*/ @Bean public JwtTokenStore jwtTokenStore() { return new JwtTokenStore(jwtAccessTokenConverter()); } @Bean public JwtAccessTokenConverter jwtAccessTokenConverter() { JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter(); jwtAccessTokenConverter.setSigningKey("cjs"); // Sets the JWT signing key return jwtAccessTokenConverter; } }
说æï¼
å«å¿äº @EnableAuthorizationServerã
Token åå¨éç¨çæ¯ JWTã
客æ·ç«¯ä»¥åç»å½ç¨æ·è¿äºé ç½®åå¨å¨æ°æ®åºï¼ä¸ºäºåå°æ°æ®åºçæ¥è¯¢æ¬¡æ°ï¼å¯ä»¥ä»æ°æ®åºè¯»åºæ¥ä»¥ååæ¾å°å åä¸ã
WebSecurityConfig(éè¦)ï¼
package com.cjs.sso.config; import com.cjs.sso.service.MyUserDetailsService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.WebSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; /** * @author ChengJianSheng * @date 2019-02-11 */ @Configuration @EnableWebSecurity public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private MyUserDetailsService userDetailsService; @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder()); } @Override public void configure(WebSecurity web) throws Exception { web.ignoring().antMatchers("/assets/**", "/css/**", "/images/**"); } @Override protected void configure(HttpSecurity http) throws Exception { http.formLogin() .loginPage("/login") .and() .authorizeRequests() .antMatchers("/login").permitAll() .anyRequest() .authenticated() .and().csrf().disable().cors(); } @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } }
èªå®ä¹ç»å½é¡µé¢(ä¸è¬æ¥è®²é½æ¯è¦èªå®ä¹ç)ï¼
package com.cjs.sso.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; /** * @author ChengJianSheng * @date 2019-02-12 */ @Controller public class LoginController { @GetMapping("/login") public String login() { return "login"; } @GetMapping("/") public String index() { return "index"; } }
èªå®ä¹ç»å½é¡µé¢çæ¶åï¼åªéè¦åå¤ä¸ä¸ªç»å½é¡µé¢ï¼ç¶åå个 Controller ä»¤å ¶å¯ä»¥è®¿é®å°å³å¯ï¼ç»å½é¡µé¢è¡¨åæäº¤çæ¶å method ä¸å®è¦æ¯ postï¼æéè¦çæ¶å action è¦è·è®¿é®ç»å½é¡µé¢ç url 䏿 ·ã
åä¸è®°ä½äºï¼è®¿é®ç»å½é¡µé¢çæ¶åæ¯ GET 请æ±ï¼è¡¨åæäº¤çæ¶åæ¯ POST 请æ±ï¼å ¶ä»çå°±ä¸ç¨ç®¡äºã
Ela Admin - HTML5 Admin Template æ¬¢è¿æ¥å°çè è£è
å®ä¹å®¢æ·ç«¯ï¼å¦ä¸å¾ï¼
å è½½ç¨æ·ï¼ç»å½è´¦æ·ï¼
package com.cjs.sso.domain; import lombok.Data; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.userdetails.User; import java.util.Collection; /** * 大é¨åæ¶åç´æ¥ç¨Userå³å¯ä¸å¿
æ©å± * @author ChengJianSheng * @date 2019-02-11 */ @Data public class MyUser extends User { private Integer departmentId; // 举个ä¾åï¼é¨é¨ID private String mobile; // 举个ä¾åï¼å设æä»¬æ³å¢å ä¸ä¸ªå段ï¼è¿éæä»¬å¢å ä¸ä¸ªmobileè¡¨ç¤ºææºå· public MyUser(String username, String password, Collection extends GrantedAuthority> authorities) { super(username, password, authorities); } public MyUser(String username, String password, boolean enabled, boolean accountNonExpired, boolean credentialsNonExpired, boolean accountNonLocked, Collection extends GrantedAuthority> authorities) { super(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities); } }
å è½½ç»å½è´¦æ·ï¼
package com.cjs.sso.service; import com.alibaba.fastjson.JSON; import com.cjs.sso.domain.MyUser; import com.cjs.sso.entity.SysPermission; import com.cjs.sso.entity.SysUser; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; import org.springframework.util.CollectionUtils; import java.util.ArrayList; import java.util.List; /** * @author ChengJianSheng * @date 2019-02-11 */ @Slf4j @Service public class MyUserDetailsService implements UserDetailsService { @Autowired private PasswordEncoder passwordEncoder; @Autowired private UserService userService; @Autowired private PermissionService permissionService; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { SysUser sysUser = userService.getByUsername(username); if (null == sysUser) { log.warn("ç¨æ·{}ä¸åå¨", username); throw new UsernameNotFoundException(username); } List permissionList = permissionService.findByUserId(sysUser.getId()); List authorityList = new ArrayList<>(); if (!CollectionUtils.isEmpty(permissionList)) { for (SysPermission sysPermission : permissionList) { authorityList.add(new SimpleGrantedAuthority(sysPermission.getCode())); } } MyUser myUser = new MyUser(sysUser.getUsername(), passwordEncoder.encode(sysUser.getPassword()), authorityList); log.info("ç»å½æåï¼ç¨æ·: {}", JSON.toJSONString(myUser)); return myUser; } }
éªè¯ï¼
彿们çå°è¿ä¸ªçé¢çæ¶åï¼è¡¨ç¤ºè®¤è¯æå¡å¨é ç½®å®æã
两个客æ·ç«¯
Maven ä¾èµï¼
4.0.0 org.springframework.boot spring-boot-starter-parent 2.1.3.RELEASE com.cjs.sso oauth2-sso-client-member 0.0.1-SNAPSHOT oauth2-sso-client-member Demo project for Spring Boot 1.8 org.springframework.boot spring-boot-starter-data-jpa org.springframework.boot spring-boot-starter-oauth2-client org.springframework.boot spring-boot-starter-security org.springframework.security.oauth.boot spring-security-oauth2-autoconfigure 2.1.3.RELEASE org.springframework.boot spring-boot-starter-thymeleaf org.thymeleaf.extras thymeleaf-extras-springsecurity5 3.0.4.RELEASE org.springframework.boot spring-boot-starter-web com.h2database h2 runtime org.projectlombok lombok true org.springframework.boot spring-boot-starter-test test org.springframework.security spring-security-test test org.springframework.boot spring-boot-maven-plugin
application.ymlï¼
server: port: 8082 servlet: context-path: /memberSystem security: oauth2: client: client-id: UserManagement client-secret: user123 access-token-uri: http://localhost:8080/oauth/token user-authorization-uri: http://localhost:8080/oauth/authorize resource: jwt: key-uri: http://localhost:8080/oauth/token_key
è¿é context-path ä¸è¦è®¾æ /ï¼ä¸ç¶éå®åè·å code çæ¶ååè¢«æ¦æªã
WebSecurityConfigï¼
package com.cjs.example.config; import com.cjs.example.util.EnvironmentUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.security.oauth2.client.EnableOAuth2Sso; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.WebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; /** * @author ChengJianSheng * @date 2019-03-03 */ @EnableOAuth2Sso @Configuration public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private EnvironmentUtils environmentUtils; @Override public void configure(WebSecurity web) throws Exception { web.ignoring().antMatchers("/bootstrap/**"); } @Override protected void configure(HttpSecurity http) throws Exception { if ("local".equals(environmentUtils.getActiveProfile())) { http.authorizeRequests().anyRequest().permitAll(); }else { http.logout().logoutSuccessUrl("http://localhost:8080/logout") .and() .authorizeRequests() .anyRequest().authenticated() .and() .csrf().disable(); } } }
说æï¼
æéè¦çæ³¨è§£æ¯ @EnableOAuth2Ssoã
æéæ§å¶è¿ééç¨ Spring Security æ¹æ³çº§å«çè®¿é®æ§å¶ï¼ç»å Thymeleaf å¯ä»¥å¾å®¹æåæéæ§å¶ã
é¡ºä¾¿å¤æä¸å¥ï¼å¦ææ¯åå端å离çè¯ï¼åç«¯éæ±å è½½ç¨æ·çæéï¼ç¶å夿åºè¯¥æ¾ç¤ºé£äºæé®é£äºèåã
MemberControllerï¼
package com.cjs.example.controller; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.core.Authentication; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import java.security.Principal; /** * @author ChengJianSheng * @date 2019-03-03 */ @Controller @RequestMapping("/member") public class MemberController { @GetMapping("/list") public String list() { return "member/list"; } @GetMapping("/info") @ResponseBody public Principal info(Principal principal) { return principal; } @GetMapping("/me") @ResponseBody public Authentication me(Authentication authentication) { return authentication; } @PreAuthorize("hasAuthority('member:save')") @ResponseBody @PostMapping("/add") public String add() { return "add"; } @PreAuthorize("hasAuthority('member:detail')") @ResponseBody @GetMapping("/detail") public String detail() { return "detail"; } }
Order 项ç®è·å®æ¯ä¸æ ·çï¼
server: port: 8083 servlet: context-path: /orderSystem security: oauth2: client: client-id: OrderManagement client-secret: order123 access-token-uri: http://localhost:8080/oauth/token user-authorization-uri: http://localhost:8080/oauth/authorize resource: jwt: key-uri: http://localhost:8080/oauth/token_key
å ³äºéåºï¼éåºå°±æ¯æ¸ 空ç¨äºä¸ SSO 客æ·ç«¯å»ºç«çææçä¼è¯ï¼ç®åçæ¥è¯´å°±æ¯ä½¿ææç«¯ç¹ç Session 失æï¼å¦ææ³å徿´å¥½çè¯å¯ä»¥ä»¤ Token 失æï¼ä½æ¯ç±äºæä»¬ç¨ç JWTï¼æ èæ¤é Token 就䏿¯é£ä¹å®¹æï¼å ³äºè¿ä¸ç¹ï¼å¨å®ç½ä¸ä¹ææå°ï¼
æ¬ä¾ä¸éç¨çæ¹å¼æ¯å¨éåºçæ¶åå éåºä¸å¡æå¡å¨ï¼æå以åååè°è®¤è¯æå¡å¨ï¼ä½æ¯è¿æ ·æä¸ä¸ªé®é¢ï¼å°±æ¯éè¦ä¸»å¨ä¾æ¬¡è°ç¨å个ä¸å¡æå¡å¨ç logoutã
å·¥ç¨ç»æ
é䏿ºç ï¼
https://github.com/chengjiansheng/cjs-oauth2-sso-demo.git
æ¼ç¤º
ä½è ï¼åºç©å¤§å¸å
ç¼è¾ï¼é¶å®¶é¾
åºå¤ï¼cnblogs.com/cjsblog/p/10548022.html















