oauth2 分为认证中心和资源服务中心,做个简单例子
一.认证中心 新增认证服务模块项目
1.引入pom
org.springframework.cloud
spring-cloud-starter-oauth2
2.0.1.RELEASE
2. 配置EnableAuthorizationServer
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
@Autowired
AuthenticationManager authenticationManager;
private static final String DEMO_RESOURCE_ID = "some_res";
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
// password 持多种编码,通过密码的前缀区分编码方式
String finalSecret = "{bcrypt}" + new BCryptPasswordEncoder().encode("123456");
//配置两个客户端,一个用于password认证一个用于client认证
//client模式,没有用户的概念,直接与认证服务器交互,用配置中的客户端信息去申请accessToken,
// 客户端有自己的client_id,client_secret对应于用户的username,password,而客户端也拥有自己的authorities,
// 当采取client模式认证时,对应的权限也就是客户端自己的authorities
clients.inMemory().withClient("client_1")
.resourceIds(DEMO_RESOURCE_ID)
.redirectUris("http://localhost:7589/login")
//.authorizedGrantTypes("authorization_code","password", "refresh_token")
//password模式,自己本身有一套用户体系,在认证时需要带上自己的用户名和密码,以及客户端的client_id,client_secret
// 此时,accessToken所包含的权限是用户本身的权限,而不是客户端的权限
.authorizedGrantTypes("authorization_code","password")
.accessTokenValiditySeconds(3600) // 设置 token 过期时间
.scopes("all")
.autoApprove(true)
.secret(finalSecret)
.and().withClient("client_2")
.resourceIds(DEMO_RESOURCE_ID)
.redirectUris("http://localhost:7589/login")
//.authorizedGrantTypes("authorization_code","password", "refresh_token")
.authorizedGrantTypes("authorization_code", "implicit","refresh_token")
.accessTokenValiditySeconds(3600) // 设置 token 过期时间
.scopes("all")
.autoApprove(false)
.secret(finalSecret)
;
}
@Bean
public TokenStore tokenStore() {
// return new JdbcTokenStore(dataSource);
return new JwtTokenStore(accessTokenConverter());
}
@Bean
public JwtAccessTokenConverter accessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setSigningKey("123");
return converter;
}
@Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
oauthServer
// 允许客户表单认证,不加的话/oauth/token无法访问
.allowFormAuthenticationForClients()
// 对于CheckEndpoint控制器[框架自带的校验]的/oauth/token端点允许所有客户端发送器请求而不会被Spring-security拦截
// 开启/oauth/token_key验证端口无权限访问
.tokenKeyAccess("permitAll()")
// 要访问/oauth/check_token必须设置为permitAll(),但这样所有人都可以访问了,设为isAuthenticated()又导致访问不了,这个问题暂时没找到解决方案
// 开启/oauth/check_token验证端口认证权限访问
.checkTokenAccess("permitAll()");
}
//定义授权和令牌端点以及令牌服务
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
// redisTokenStore
// endpoints.tokenStore(new RedisTokenStore(redisConnectionFactory))
// .authenticationManager(authenticationManager)
// .allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST);
endpoints
//指定token存储位置
.tokenStore(tokenStore())
// 配置JwtAccessToken转换器
.tokenEnhancer(accessTokenConverter())
//指定认证管理器
.authenticationManager(authenticationManager)
.allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST);
;
// 配置tokenServices参数
DefaultTokenServices tokenServices = new DefaultTokenServices();
tokenServices.setTokenStore(endpoints.getTokenStore());
// 是否支持刷新
tokenServices.setSupportRefreshToken(true);
tokenServices.setClientDetailsService(endpoints.getClientDetailsService());
tokenServices.setTokenEnhancer(endpoints.getTokenEnhancer());
// 20分钟
tokenServices.setAccessTokenValiditySeconds((int) TimeUnit.MINUTES.toSeconds(20));
endpoints.tokenServices(tokenServices);
}
}
3.配置用户认证权限
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class AuthWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
@Autowired
private SsoUserDetailsService ssoUserDetailsService;
/**
* password 支持多种编码,通过密码的前缀区分编码方式,推荐
*/
@Bean
PasswordEncoder passwordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
/**
* 这一步的配置是必不可少的,否则SpringBoot会自动配置一个AuthenticationManager,覆盖掉内存中的用户
*/
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
AuthenticationManager manager = super.authenticationManagerBean();
return manager;
}
@Autowired
public void config(AuthenticationManagerBuilder auth) throws Exception {
//设置UserDetailsService以及密码规则
auth.userDetailsService(ssoUserDetailsService).passwordEncoder(passwordEncoder());
}
}
@Component
public class SsoUserDetailsService implements UserDetailsService {
@Autowired
private PasswordEncoder passwordEncoder;
@Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
if("qzq".equals(s)){
return new User( s, passwordEncoder.encode("123456"), AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_NORMAL,ROLE_ADMIN"));
}else{
return new User( s, passwordEncoder.encode("123456"), AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_NORMAL"));
}
}
}
4.最后配置yml 本地测试的话 context-path一定要配置
server:
port: 7585
servlet:
#这里一定要配置,否则本地IP访问授权会不成
context-path: /sso
spring:
application:
name: cloud-auth-service
logging:
level:
root: debug
二,接下来新增客户端,这里客户端包括了资源权限认证
1.同样引入pom
2.配置WebSecurityConfigurerAdapter
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
@EnableOAuth2Sso
public class AuthWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
@Autowired
private TokenStore tokenStore;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.antMatcher("/**").authorizeRequests()
.anyRequest().authenticated()
.withObjectPostProcessor(new ObjectPostProcessor() {
@Override
public O postProcess(O fsi) {
// 权限获取自定义配置
fsi.setSecurityMetadataSource(new PermissionFilterInvocationSecurityMetadataSource());
return fsi;
}
})
.accessDecisionManager(accessDecisionManager());
}
private AccessDecisionManager accessDecisionManager() {
WebExpressionVoter webExpressionVoter = new WebExpressionVoter();
webExpressionVoter.setExpressionHandler(new OAuth2WebSecurityExpressionHandler());
// 授权逻辑自定义配置
return new AffirmativeBased(Lists.newArrayList(new PermissionsVoter(), new RoleVoter(),
new AuthenticatedVoter(), webExpressionVoter));
}
@Primary
@Bean
public DefaultTokenServices tokenServices() {
DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
defaultTokenServices.setTokenStore(tokenStore);
return defaultTokenServices;
}
}
2.配置动态资源权限
public class PermissionFilterInvocationSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {
private static final Map> urlMap = new HashMap<>();
static {
//测试用 可以从数据库获取
AntPathRequestMatcher url = new AntPathRequestMatcher("/api/testResourcePower");
urlMap.put(url,Lists.newArrayList(new SecurityConfig("ROLE_ADMIN")));
}
public PermissionFilterInvocationSecurityMetadataSource() {
}
/**
* 转换权限列表
*/
private Map> requestMatcherCollectionMap() {
return urlMap;
}
@Override
public Collection getAttributes(Object object) throws IllegalArgumentException {
final HttpServletRequest request = ((FilterInvocation) object).getRequest();
for (Map.Entry> entry : requestMatcherCollectionMap().entrySet()) {
if (entry.getKey().matches(request)) {
return entry.getValue();
}
}
return null;
}
@Override
public Collection getAllConfigAttributes() {
return requestMatcherCollectionMap().values()
.stream().flatMap(Collection::stream)
.collect(Collectors.toList());
}
@Override
public boolean supports(Class clazz) {
return FilterInvocation.class.isAssignableFrom(clazz);
}
}
@Slf4j
public class PermissionsVoter implements AccessDecisionVoter
3.yml配置
server:
port: 7589
spring:
application:
name: cloud-auth-client
main:
allow-bean-definition-overriding: true
auth-server: http://localhost:7585/sso
security:
oauth2:
client:
client-id: client_2
client-secret: 123456
user-authorization-uri: ${auth-server}/oauth/authorize
access-token-uri: ${auth-server}/oauth/token
resource:
jwt:
key-uri: ${auth-server}/oauth/token_key
scope: all
logging:
level:
root: debug
4.写controller测试 这里路径在PermissionFilterInvocationSecurityMetadataSource动态设置权限了
//http://localhost:7589/api/testResourcePower
@GetMapping("/testResourcePower")
public String testResourcePower() {
//for debug
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
log.info("authentication: " + authentication.getAuthorities().toString());
return "testResourcePower";
}
5.访问路径http://localhost:7589/api/testResourcePower成功跳转到认证中心登录页面,输入用户、密码成功访问接口
源码传送门 https://github.com/fdqzq613/myframe.git