我使用Spring Security Cloud和Angular 2客户端在OAuth2的授权代码流程上做了一个小演示。
一切正常,我从服务器获得访问令牌响应。
然而,根据Aaron perecki的博客https://aaronparecki.com/oauth-2-simplified/
从网页加载源代码后,单页应用(或基于浏览器的应用)完全在浏览器中运行。由于整个源代码可供浏览器使用,因此无法保持其客户机密的机密性,因此在这种情况下不使用该机密。该流程与上面的授权代码流程完全相同,但在最后一步,授权代码将交换访问令牌而不使用客户端密钥。
因此,我不想使用客户端密钥从auth-server获取访问权限。
但是,如果没有与auth服务器共享客户端密钥,我将无法继续。
以下是我的Angular 2逻辑来检索令牌
import {Injectable} from '@angular/core';
import {IUser} from './user';
import {Router} from '@angular/router';
import {Http, RequestOptions, Headers, URLSearchParams} from '@angular/http';
@Injectable()
export class AuthService {
currentUser: IUser;
redirectUrl: string;
state: string;
tokenObj: any;
constructor(private router: Router, private http: Http) {
this.state = '43a5';
}
isLoggedIn(): boolean {
return !!this.currentUser;
}
loginAttempt(username: string, password: string): void {
const credentials: IUser = {
username: username,
password: password
};
const params = new URLSearchParams();
params.append('client_id', 'webapp');
params.append('redirect_uri', 'http://localhost:9090/callback');
params.append('scope', 'read');
params.append('grant_type', 'authorization_code');
params.append('state', this.state);
params.append('response_type', 'code');
const headers = new Headers({
'Authorization': 'Basic ' + btoa(username + ':' + password)
});
const options = new RequestOptions({headers: headers});
this.http.post('http://localhost:9090/oauth/authorize', params, options)
.subscribe(
data => {
const authresponse = data.json();
this.tokenObj = this.getTokens(authresponse.code).json();
},
err => console.log(err)
);
}
getTokens(code: string): any {
const params = new URLSearchParams();
params.append('grant_type', 'authorization_code');
params.append('code', code);
params.append('redirect_uri', 'http://localhost:9090/callback');
const headers = new Headers({
'Authorization': 'Basic ' + btoa('webapp:websecret')
});
const options = new RequestOptions({headers: headers});
this.http.post('http://localhost:9090/oauth/token', params, options)
.subscribe(
data => {
return data.json();
},
err => console.log(err)
);
}
logout(): void {
this.currentUser = null;
}
}
这是我的 AuthorizationServerConfig 类源代码
@Configuration
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
@Autowired
private AuthenticationManager authManager;
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.accessTokenConverter(accessTokenConverter()).authenticationManager(authManager);
}
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security.checkTokenAccess("permitAll()");
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.jdbc(dataSource());
}
@Bean
public TokenStore tokenStore() {
return new JwtTokenStore(accessTokenConverter());
}
@Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/oauth2?createDatabaseIfNotExist=true");
dataSource.setUsername("root");
dataSource.setPassword("chandra");
return dataSource;
}
@Bean
public JwtAccessTokenConverter accessTokenConverter() {
JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
converter.setSigningKey("123");
return converter;
}
@Bean
@Primary
public DefaultTokenServices tokenServices() {
DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
defaultTokenServices.setTokenStore(tokenStore());
defaultTokenServices.setSupportRefreshToken(true);
return defaultTokenServices;
}
}
WebConfig类的源代码
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser("user1").password("password1").roles("USER")
.and().withUser("admin1").password("password1").roles("ADMIN");
auth.eraseCredentials(false);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/oauth/**").permitAll()
.anyRequest().authenticated()
.and().csrf().disable()
.httpBasic();
}
}
SpringBootApplication类
@SpringBootApplication
@EnableAuthorizationServer
@RestController
public class SpringMicroservicesOauthServerApplication {
public static void main(String[] args) {
SpringApplication.run(SpringMicroservicesOauthServerApplication.class, args);
}
@RequestMapping("callback")
public AuthCodeResponse test(@RequestParam("code") String code, @RequestParam("state") String state) {
return new AuthCodeResponse(code,state);
}
}
AuthCodeResponse POJO
public class AuthCodeResponse {
private String code;
private String state;
public AuthCodeResponse() {
}
public AuthCodeResponse(String code, String state) {
this.code = code;
this.state = state;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
}
}
答案 0 :(得分:1)
我没有在那里看到一个实际的问题,但如果您只想隐藏客户端密码,为什么不创建自己的API,那就是您处理所有OAuth2的东西,从而保持客户秘密,很隐秘。
那是你从Angular前端调用的那个。
您完全不需要在JavaScript中使用它来完全暴露令牌。
是的,这是一个额外的步骤,但如果你想保持安全,那么完全值得。
答案 1 :(得分:0)
如果 4.1. Authorization Code Grant RFC 6749中“client type”中定义的授权代码流不需要client_secret
>您的申请公开。
但是,即使您的应用程序的客户端类型是公共的,您的授权服务器也需要一对API密钥和API密钥。为什么?这是因为WebSecurityConfig
正在保护/oauth/**
。即使/oauth/**
不是OAuth端点,也会执行保护。
(a)客户ID和客户机密的保护和(b)通用方式的保护(在这种情况下,WebSecurityConfig
的保护)是不同的事情。