單點(diǎn)登錄(英語:Single sign-on,縮寫為 SSO),又譯為單一簽入,一種對于許多相互關(guān)連,但是又是各自獨(dú)立的軟件系統(tǒng),提供訪問控制的屬性。當(dāng)擁有這項(xiàng)屬性時,當(dāng)用戶登錄時,就可以獲取所有系統(tǒng)的訪問權(quán)限,不用對每個單一系統(tǒng)都逐一登錄。這項(xiàng)功能通常是以輕型目錄訪問協(xié)議(LDAP)來實(shí)現(xiàn),在服務(wù)器上會將用戶信息存儲到LDAP數(shù)據(jù)庫中。相同的,單一注銷(single sign-off)就是指,只需要單一的注銷動作,就可以結(jié)束對于多個系統(tǒng)的訪問權(quán)限。
Security OAuth2 單點(diǎn)登錄流程示意圖
- 訪問client1
-
client1將請求導(dǎo)向sso-server - 同意授權(quán)
- 攜帶授權(quán)碼
code返回client1 -
client1拿著授權(quán)碼請求令牌 - 返回
JWT令牌 -
client1解析令牌并登錄 -
client1訪問client2 -
client2將請求導(dǎo)向sso-server - 同意授權(quán)
- 攜帶授權(quán)碼
code返回client2 -
client2拿著授權(quán)碼請求令牌 - 返回
JWT令牌 -
client2解析令牌并登錄
用戶的登錄狀態(tài)是由sso-server認(rèn)證中心來保存的,登錄界面和賬號密碼的驗(yàn)證也是sso-server認(rèn)證中心來做的(client1和clien2返回token是不同的,但解析出來的用戶信息是同一個用戶)。
Security OAuth2 實(shí)現(xiàn)單點(diǎn)登錄
項(xiàng)目結(jié)構(gòu)
sso-server
認(rèn)證服務(wù)器
@Configuration
@EnableAuthorizationServer
public class SsoAuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
/**
* 客戶端一些配置
* @param clients
* @throws Exception
*/
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("merryyou1")
.secret("merryyousecrect1")
.authorizedGrantTypes("authorization_code", "refresh_token")
.scopes("all")
.and()
.withClient("merryyou2")
.secret("merryyousecrect2")
.authorizedGrantTypes("authorization_code", "refresh_token")
.scopes("all");
}
/**
* 配置jwttokenStore
* @param endpoints
* @throws Exception
*/
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.tokenStore(jwtTokenStore()).accessTokenConverter(jwtAccessTokenConverter());
}
/**
* springSecurity 授權(quán)表達(dá)式,訪問merryyou tokenkey時需要經(jīng)過認(rèn)證
* @param security
* @throws Exception
*/
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security.tokenKeyAccess("isAuthenticated()");
}
/**
* JWTtokenStore
* @return
*/
@Bean
public TokenStore jwtTokenStore() {
return new JwtTokenStore(jwtAccessTokenConverter());
}
/**
* 生成JTW token
* @return
*/
@Bean
public JwtAccessTokenConverter jwtAccessTokenConverter(){
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setSigningKey("merryyou");
return converter;
}
}
security配置
@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().loginPage("/authentication/require")
.loginProcessingUrl("/authentication/form")
.and().authorizeRequests()
.antMatchers("/authentication/require",
"/authentication/form",
"/**/*.js",
"/**/*.css",
"/**/*.jpg",
"/**/*.png",
"/**/*.woff2"
)
.permitAll()
.anyRequest().authenticated()
.and()
.csrf().disable();
// http.formLogin().and().authorizeRequests().anyRequest().authenticated();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}
}
SsoUserDetailsService
@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"));
}
}
application.yml
server:
port: 8082
context-path: /uaa
spring:
freemarker:
allow-request-override: false
allow-session-override: false
cache: true
charset: UTF-8
check-template-location: true
content-type: text/html
enabled: true
expose-request-attributes: false
expose-session-attributes: false
expose-spring-macro-helpers: true
prefer-file-system-access: true
suffix: .ftl
template-loader-path: classpath:/templates/
sso-client1
SsoClient1Application
@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);
}
}
application.yml
auth-server: http://localhost:8082/uaa # sso-server地址
server:
context-path: /client1
port: 8083
security:
oauth2:
client:
client-id: merryyou1
client-secret: merryyousecrect1
user-authorization-uri: ${auth-server}/oauth/authorize #請求認(rèn)證的地址
access-token-uri: ${auth-server}/oauth/token #請求令牌的地址
resource:
jwt:
key-uri: ${auth-server}/oauth/token_key #解析jwt令牌所需要密鑰的地址
sso-client2
同sso-client1一致
效果如下:
[圖片上傳失敗...(image-1c6041-1516894354927)]
代碼下載
從我的 github 中下載,https://github.com/longfeizheng/sso-merryyou

