一 . 概述
单点登录(Singal Sign On)是很多企业经常使用的一种登录方式,那么何为单点登录呢?为了解决什么样的问题呢?举个例子,在淘宝公司内部,有天猫、淘宝、阿里云、聚划算等众多的产品线,这些产品线是不同的服务器来支撑运行,但是作为用户的我们却可以使用同一套账户名和密码进行登录他几乎所有的产品,登录服务器只有一个,根据登录服务器返回的特定的信息,可以去访问它所有的产品线。着就是所谓的单点登录。
1.1 认证服务器
依赖
org.springframework.boot
spring-boot-starter-web
org.springframework.security.oauth
spring-security-oauth2
2.3.6.RELEASE
org.springframework.security.oauth.boot
spring-security-oauth2-autoconfigure
2.1.9.RELEASE
org.slf4j
slf4j-api
Yml配置
server:
port: 7070
servlet:
context-path: /auth
授权服务
@EnableAuthorizationServer
@Configuration
public class JwtServerConfig extends AuthorizationServerConfigurerAdapter {
@Autowired
private PasswordEncoder passwordEncoder;
// jwt的生成器
@Bean
public JwtAccessTokenConverter jwtAccessTokenConverter() {
JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();
jwtAccessTokenConverter.setSigningKey("abcxyz");
return jwtAccessTokenConverter;
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.accessTokenConverter(jwtAccessTokenConverter()) //配置JWT的生成器
.allowedTokenEndpointRequestMethods(HttpMethod.POST, HttpMethod.GET);
}
// 客户端的配置
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory() //
.withClient("client-a") // client_id
.secret("$2a$10$GqJuJhe7zmtwwThIed7smu9zMJBgSQzFMP47eEDL.g9tg8Y82.A7m") // client_secret
.authorizedGrantTypes("authorization_code") //采用什么方式获取access_token
.redirectUris("http://localhost:8081/clientA/login")
.scopes("all")
.autoApprove(true) //自动授权
.and()
.withClient("client-b") // client_id
.secret("$2a$10$GqJuJhe7zmtwwThIed7smu9zMJBgSQzFMP47eEDL.g9tg8Y82.A7m") // client_secret
.authorizedGrantTypes("authorization_code") //采用什么方式获取access_token
.redirectUris("http://localhost:8082/clientB/login")
.scopes("all")
.autoApprove(true);
}
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security.allowFormAuthenticationForClients()
.checkTokenAccess("isAuthenticated()")
.passwordEncoder(passwordEncoder);
}
}
安全配置
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
/**
* 该bean的作用是,在UserDetailsService接口的loadUserByUsername返回的UserDetail中包含了
* password, 该bean就将用户从页面提交过来的密码进行处理,处理之后与UserDetail中密码进行比较。
* @return
*/
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
// 统一安全配置
@Override
protected void configure(HttpSecurity http) throws Exception {
// 意思是将图片验证码过滤器,加载用户名密码验证过滤器之前
http.formLogin() //使用form进行登录
.loginPage("/login.html") //指定登录页面
.loginProcessingUrl("/authentication/form") //表示form往哪里进行提交
.and()
.authorizeRequests()
.antMatchers("/login.html", "/images/**").permitAll()
.anyRequest()
.authenticated()
.and()
.csrf().disable();
}
// 密码加密处理
public static void main(String[] args) {
BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
System.out.println(bCryptPasswordEncoder.encode("1"));
}
}
自定义认证
// 该类的作用是处理用户登录名和密码
@Component
public class UserSecurityService implements UserDetailsService {
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = new User(username,
"$2a$10$GqJuJhe7zmtwwThIed7smu9zMJBgSQzFMP47eEDL.g9tg8Y82.A7m",
Arrays.asList(new SimpleGrantedAuthority("ROLE_user"), new SimpleGrantedAuthority("ROLE_admin")));
return user;
}
}
登录页面
用户登录
1.2 子系统client-a
依赖
org.springframework.boot
spring-boot-starter-web
org.springframework.security.oauth
spring-security-oauth2
2.3.6.RELEASE
org.springframework.boot
spring-boot-starter-security
org.springframework.security.oauth.boot
spring-security-oauth2-autoconfigure
2.1.9.RELEASE
配置
server:
port: 8081
servlet:
context-path: /clientA
security:
oauth2:
client:
client-id: client-a
client-secret: 1
# 在每个服务器中要保存 access_token
access-token-uri: http://localhost:7070/auth/oauth/token
user-authorization-uri: http://localhost:7070/auth/oauth/authorize
resource:
jwt:
key-value: abcxyz #验签
启动类配置
@SpringBootApplication
@EnableOAuth2Sso
// 启动权限认证
@EnableGlobalMethodSecurity(prePostEnabled = true, jsr250Enabled = true, securedEnabled=true)
public class SsoClientApplicationA {
public static void main(String[] args) {
SpringApplication.run(SsoClientApplicationA.class, args);
}
}
controller示例
@RequestMapping
@RestController
public class UserController {
@RequestMapping("/user")
@PreAuthorize("hasAuthority('ROLE_admin')")
public Object get(Authentication authentication) {
System.out.println("ClientA: " + authentication.getName() + ";;" + authentication.getAuthorities());
return "clientA";
}
@RequestMapping("/login")
@PreAuthorize("hasAuthority('ROLE_admin')")
public Object hello(Authentication authentication) {
System.out.println("ClientA: " + authentication.getName() + ";;" + authentication.getAuthorities());
return "hello_A";
}
}
index.html
Title
应用AAAA
访问B服务
1.2 子系统client-b
依赖
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-security
org.springframework.security.oauth
spring-security-oauth2
2.3.6.RELEASE
org.springframework.security.oauth.boot
spring-security-oauth2-autoconfigure
2.1.9.RELEASE
yml配置
server:
port: 8082
servlet:
context-path: /clientB
security:
oauth2:
client:
client-id: client-b
client-secret: 1
access-token-uri: http://localhost:7070/auth/oauth/token
user-authorization-uri: http://localhost:7070/auth/oauth/authorize
resource:
jwt:
key-value: abcxyz #验签
启动类配置
@SpringBootApplication
@EnableOAuth2Sso
@EnableGlobalMethodSecurity(prePostEnabled = true, jsr250Enabled = true, securedEnabled=true)
public class SsoClientApplicationB {
public static void main(String[] args) {
SpringApplication.run(SsoClientApplicationB.class, args);
}
}
controller示例
@RestController
@RequestMapping
public class UserController {
@RequestMapping("/info")
@RolesAllowed("ROLE_admin")
public Object get(Authentication authentication) {
System.out.println("ClientB: " + authentication.getName() + ";;" + authentication.getAuthorities());
return "clientA";
}
@RequestMapping
@RolesAllowed("ROLE_admin")
public Object hello(Authentication authentication) {
System.out.println("ClientB: " + authentication.getName() + ";;" + authentication.getAuthorities());
return "hello_B";
}
}
index.html
Title
应用B
访问A服务
源码: 链接:https://pan.baidu.com/s/1hDeo-gMwuStKaL1mniUDog&shfl=sharepset 密码:t4s4