我使用带有 Angular 2 客户端的 Spring Security Cloud 对 OAuth2 的授权代码流程做了一个小演示。
一切正常,我正在从服务器获取访问令牌响应。
然而,根据 Aaron perecki 的博客https://aaronparecki.com/oauth-2-simplified/
从网页加载源代码后,单页应用程序(或基于浏览器的应用程序)完全在浏览器中运行。由于浏览器可以使用整个源代码,因此它们无法维护其客户端机密的机密性,因此在这种情况下不使用机密。该流程与上面的授权码流程完全相同,但在最后一步,将授权码交换为访问令牌,而不使用客户端密钥。
所以我不想使用客户端密码从 auth-server 获取访问令牌。
但是,如果不将客户端密码共享给身份验证服务器,我将无法继续。
以下是我检索令牌的 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;
}
}