社区微信群开通啦,扫一扫抢先加入社区官方微信群
社区微信群
1.介绍
(1) eureka服务发现,各服务配置就不发了,只看关于认证这块;
(2)
服务名 | 端口号 | 备注 |
auth | 8082 | 认证服务器 |
mechant | 8081 | 资源服务器 |
zuul | 80 | 网关 |
2 配置认证服务器
(1)添加依赖(oauth已经包含了security)
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
(2)配置认证
@Configuration
public class MyConfig {
//设置用户信息处理类,这里为了测试使用密码123,用户名随意
@Component
public static class MyUserDetailsService implements UserDetailsService {
@Autowired
private PasswordEncoder passwordEncoder;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return new User(username, passwordEncoder.encode("123"),
AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_USER"));
}
}
//认证服务器
@EnableAuthorizationServer
@Configuration
public static class Authorization extends AuthorizationServerConfigurerAdapter {
@Autowired
AuthenticationManager authenticationManager;
@Autowired
BCryptPasswordEncoder bCryptPasswordEncoder;
@Autowired
MyUserDetailsService myUserDetailsService;
//为了测试客户端与凭证存储在内存(生产应该用数据库来存储,oauth有标准数据库模板)
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("client") // client_id
.secret(bCryptPasswordEncoder.encode("123")) // client_secret
.authorizedGrantTypes("authorization_code", "password") // 该client允许的授权类型
.scopes("app"); // 允许的授权范围
}
//authenticationManager配合password模式使用,tokenstore生产可用redis
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager)
.tokenStore(new InMemoryTokenStore())
.userDetailsService(myUserDetailsService);
}
//配置token状态查询
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager)
.tokenStore(new InMemoryTokenStore())
.userDetailsService(myUserDetailsService);
endpoints.allowedTokenEndpointRequestMethods(HttpMethod.POST,HttpMethod.GET);
DefaultTokenServices tokenService = new DefaultTokenServices();
tokenService.setTokenStore(endpoints.getTokenStore());
tokenService.setSupportRefreshToken(true);
tokenService.setClientDetailsService(endpoints.getClientDetailsService());
tokenService.setTokenEnhancer(endpoints.getTokenEnhancer());
tokenService.setAccessTokenValiditySeconds((int) TimeUnit.DAYS.toSeconds(30)); //30天
tokenService.setRefreshTokenValiditySeconds((int) TimeUnit.DAYS.toSeconds(50)); //50天
tokenService.setReuseRefreshToken(false);
endpoints.tokenServices(tokenService);
}
//认证服务器需配合Security使用
@Configuration
public static class SecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
//这里只验证是否带有token的失败返回authenticationEntryPoint
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.httpBasic().and()
.csrf().disable()
.exceptionHandling()
.authenticationEntryPoint((req, resp, exception) -> {
resp.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
resp.getWriter().write(new ObjectMapper().writeValueAsString(new HashMap() {{
put("status", 0);
put("error", "没有权限");
}}));
}).and()
.authorizeRequests().anyRequest().authenticated();
}
}
}
(3)提供客户信息
@RestController
public class ResourceWeb {
@GetMapping("/member")
public Principal user(Principal member) {
//获取当前用户信息
return member;
}
}
(4)流程
这里用的是密码方式,一般用于本地服务自身资源的调用;
(client,secret)代表客户端账号密码,在该测试中,这个客户端其实就表示本地的服务.如果是第三方,则申请到账号密码后,可以在得到用户授权后进行资源调用;
(username,password)就是本服务的用户主体了,所有客户端获取资源都需要本地用户的登陆成功授权后方可获取,所以用到了security的登陆策略;
3.配置资源服务器(本地资源)
(1)同样添加oauth依赖
(2)配置远程认证服务:
security:
oauth2:
resource:
user-info-uri: http://localhost:8082/member
prefer-token-info: false
(3)增加自定义异常返回,以及能够验证oauth2的参数
@Configuration
public class MyConfig {
//配置资源服务器
@Configuration
@EnableResourceServer
public static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http.csrf().disable().exceptionHandling()
.authenticationEntryPoint(new EntryPint())
.and().authorizeRequests()
.antMatchers("/manage/shutdown", "/open/**")
.access("#oauth2.hasScope('app')").anyRequest().permitAll();
}
@Override
public void configure(ResourceServerSecurityConfigurer resources) throws Exception {
resources.accessDeniedHandler((request,response,e)->{
response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
response.getWriter().write(new ObjectMapper().writeValueAsString(BaseResult.failure("授权失败")));
});
resources.authenticationEntryPoint((request,response,e)->{
response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
response.getWriter().write(new ObjectMapper().writeValueAsString(BaseResult.failure("token失败")));
});
}
}
4.配置zuul
zuul:
#routes:
# MECHANT:
# service-id: MECHANT
# path: /mechant/**
strip-prefix: true #当为false时,请求地址->MECHANT->http://localhost:8081/api/mechant/ping,返回404
prefix: /api #请求前缀
sensitive-headers: #此处不写则无法携带header;如果客户端在发请求是带了X-ABC,那么X-ABC不会传递给下游服务
#ignoredHeaders: X-ABC #如果客户端在发请求是带了X-ABC,那么X-ABC依然会传递给下游服务。但是如果下游服务再转发就会被过滤
5.测试
(1)申请token(使用zuul访问)
或者直接使用纯表单方式提交,不带header:
(2)如果是客户端模式(client_credentials),申请token如下:
(3)使用token(使用zuul访问)
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!