spring cloud 对外服务的统一认证,以及各微服务之间相互调用的身份认证,需要有个认证服务器,上两节学习了以cas中央认证服务器作认证,作为spring cloud全家桶,本身提供了oauth2的统一认证,能很好地集成在整个微服务集群中,这节就学习oauth2服务器的搭建
1. 新建spring boot start project 我这设置项目名为:MicroserviceOauth2Server8301
2. pom.xml 加入以下依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
3. application.properties 配置
server.port: 8301
spring.application.name=MicroserviceOauth2Server8301
spring.cloud.discovery.enabled=true
spring.redis.host= centos7.linbsoft.com
spring.redis.port=6379
eureka.client.serviceUrl.defaultZone=http://admin:123@centos7.linbsoft.com:8101/eureka/,http://admin:123@microservice1.linbsoft.com:8102/eureka/
logging.level.org.springframework.security=DEBUG
4. 启动类 MicroserviceOauth2Server8301Application
@EnableDiscoveryClient
@SpringBootApplication
public class MicroserviceOauth2Server8301Application {
public static void main(String[] args) {
SpringApplication.run(MicroserviceOauth2Server8301Application.class, args);
}
}
5. 授权服务配置类AuthorizationServerConfig,这里的登录客户是写死在内存的,实际部署可以通过数据库读取。
-
@Configuration
-
@EnableAuthorizationServer
-
public
class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
-
@Autowired
-
private AuthenticationManager authenticationManager;
// 认证管理器
-
-
@Autowired
-
private RedisConnectionFactory redisConnectionFactory;
// redis连接工厂
-
-
/**
-
* 令牌存储
-
* @return redis令牌存储对象
-
*/
-
@Bean
-
public TokenStore tokenStore() {
-
return
new RedisTokenStore(redisConnectionFactory);
-
}
-
-
@Override
-
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
-
endpoints.authenticationManager(
this.authenticationManager);
-
endpoints.tokenStore(tokenStore());
-
}
-
-
@Override
-
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
-
security
-
.tokenKeyAccess(
"permitAll()")
-
.checkTokenAccess(
"isAuthenticated()");
-
}
-
-
@Override
-
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
-
clients.inMemory()
-
.withClient(
"admin")
-
.scopes(
"read")
-
.secret(
new BCryptPasswordEncoder().encode(
"123456"))
-
.authorizedGrantTypes(
"password",
"authorization_code",
"refresh_token")
-
.and()
-
.withClient(
"webapp")
-
.scopes(
"xx")
-
.authorizedGrantTypes(
"implicit");
-
}
-
-
}
6. 应用服务器网站本身的安全配置类SecurityConfig
-
Configuration
-
@EnableWebSecurity
-
@EnableGlobalMethodSecurity(prePostEnabled =
true)
-
public
class SecurityConfig extends WebSecurityConfigurerAdapter {
-
-
@Bean
-
public UserDetailsService userDetailsService() {
-
return
new CustomUserDetailsService();
-
}
-
-
@Bean
-
public PasswordEncoder passwordEncoder() {
-
// return PasswordEncoderFactories.createDelegatingPasswordEncoder();
-
return
new BCryptPasswordEncoder();
-
}
-
-
@Autowired
-
public void globalUserDetails(AuthenticationManagerBuilder auth) throws Exception {
-
// auth.userDetailsService(userDetailsService()).passwordEncoder(passwordEncoder());
-
auth.userDetailsService(userDetailsService());
-
}
-
-
@Override
-
@Bean
-
public AuthenticationManager authenticationManagerBean() throws Exception {
-
return
super.authenticationManagerBean();
-
}
-
-
-
@Override
-
protected void configure(HttpSecurity http) throws Exception {
-
http.authorizeRequests().antMatchers(HttpMethod.OPTIONS).permitAll().anyRequest().authenticated().and()
-
.httpBasic().and().csrf().disable();
-
}
7. oauth2本身也是一个资源服务器,配置类ResourceServerConfig
-
@Configuration
-
@EnableResourceServer
-
public
class ResourceServerConfig extends ResourceServerConfigurerAdapter {
-
-
@Override
-
public void configure(HttpSecurity http) throws Exception {
-
http.requestMatcher(
new OAuth2RequestedMatcher())
-
.authorizeRequests()
-
.antMatchers(HttpMethod.OPTIONS).permitAll()
-
.anyRequest().authenticated();
-
}
-
-
-
private
static
class OAuth2RequestedMatcher implements RequestMatcher {
-
@Override
-
public boolean matches(HttpServletRequest request) {
-
String auth = request.getHeader(
"Authorization");
-
boolean haveOauth2Token = (auth !=
null) && auth.startsWith(
"Bearer");
-
boolean haveAccessToken = request.getParameter(
"access_token")!=
null;
-
return haveOauth2Token || haveAccessToken;
-
}
-
}
-
-
}
8. 自定义用户服务类CustomUserDetailsService ,实例化方法 loadUserByUsername ,本方法的作用是根据登录用户名,查找获取用户类,包含用户账号,密码,权限等,这个一般需要查找数据库获取(与第5步的获取登录账号密码一致),这里demo就直接生成一个固定的用户测试。
-
@Service
-
public
class CustomUserDetailsService implements UserDetailsService {
-
-
@Override
-
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
-
System.out.println(
"当前的用户名是:"+username);
-
//这里为了方便测试,就直接返回一个用户信息,实际当中这里修改为查询数据库或者调用服务什么的来获取用户信息
-
//根据登录名 到数据库取出用户信息
-
User user = mockUser();
-
return user;
-
}
-
// BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
-
private User mockUser() {
-
Collection<GrantedAuthority> authorities =
new HashSet<>();
-
authorities.add(
new SimpleGrantedAuthority(
"admin-role"));
//用户所拥有的角色信息
-
User user =
new User(
"admin",
new BCryptPasswordEncoder().encode(
"123456"),authorities);
-
// User user = new User("admin","12345",authorities);
-
return user;
-
}
-
}
9. 通过验证后,获取用户 map为 “/user”
@RestController
public class UserController {
@GetMapping("/user")
public Principal user(Principal user){
return user;
}
}
10. 测试
首先可以通过Postman 测试api接口
以上是正常获取登录用户信息
也可以通过浏览器测试,输入账号密码后,即可看到保护的资源,这里是用户信息
测试流程:
1. 获取code
弹出登录界面,登录后获取code
2. 获取token,post方法提交,把获取的code写入
获取到token
{
"access_token": "e86e7551-a01f-42af-830e-78f41c473bb8",
"token_type": "bearer",
"refresh_token": "647ddcaf-3416-47f7-8264-ebad1a374152",
"expires_in": 42541,
"scope": "read"
}
到redis查看到缓存的token
127.0.0.1:6379> keys *
1) "uname_to_access:admin:admin"
2) "auth:e86e7551-a01f-42af-830e-78f41c473bb8"
3) "client_id_to_access:admin"
4) "refresh:647ddcaf-3416-47f7-8264-ebad1a374152"
5) "access:e86e7551-a01f-42af-830e-78f41c473bb8"
6) "refresh_to_access:647ddcaf-3416-47f7-8264-ebad1a374152"
7) "refresh_auth:647ddcaf-3416-47f7-8264-ebad1a374152"
8) "access_to_refresh:e86e7551-a01f-42af-830e-78f41c473bb8"
9) "auth_to_access:35eb42947eaa87475ff2746ebe2a93f1"
3.用获取的token 访问资源
http://centos7.linbsoft.com:8301/user?access_token=e86e7551-a01f-42af-830e-78f41c473bb8
返回如下:
{"authorities":[{"authority":"admin-role"}],"details":{"remoteAddress":"192.168.49.141","sessionId":"724EE7828B6D43CD6D76C52101895155","tokenValue":"e86e7551-a01f-42af-830e-78f41c473bb8","tokenType":"Bearer","decodedDetails":null},"authenticated":true,"userAuthentication":{"authorities":[{"authority":"admin-role"}],"details":{"remoteAddress":"192.168.49.141","sessionId":null},"authenticated":true,"principal":{"password":null,"username":"admin","authorities":[{"authority":"admin-role"}],"accountNonExpired":true,"accountNonLocked":true,"credentialsNonExpired":true,"enabled":true},"credentials":null,"name":"admin"},"oauth2Request":{"clientId":"admin","scope":["read"],"requestParameters":{"code":"WgI15s","grant_type":"authorization_code","response_type":"code","redirect_uri":"http://centos7.linbsoft.com:8301","client_id":"admin"},"resourceIds":[],"authorities":[],"approved":true,"refresh":false,"redirectUri":"http://centos7.linbsoft.com:8301","responseTypes":["code"],"extensions":{},"grantType":"authorization_code","refreshTokenRequest":null},"principal":{"password":null,"username":"admin","authorities":[{"authority":"admin-role"}],"accountNonExpired":true,"accountNonLocked":true,"credentialsNonExpired":true,"enabled":true},"credentials":"","clientOnly":false,"name":"admin"}
本学习参考多位网友文章,在此不一一列出,谢谢。