1、依赖,使用Maven
<dependency>
<groupId>org.springframework.security.oauth</groupId>
<artifactId>spring-security-oauth2</artifactId>
</dependency>
2、Resource Server 配置
@Configuration
@EnableResourceServer
public class ResourceServerConfig extends ResourceServerConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/api/menu_parent/**").authenticated();
}
}
3、Authorization Server 配置,这里使用数据库持久化token信息,建表sql在后面,为了解决获取token时的ClientAlreadyExistsException异常,重写了JdbcClientDetailsServiceBuilder类,主要区别是
@Override
protected ClientDetailsService performBuild() {
Assert.state(dataSource != null, "You need to provide a DataSource");
JdbcClientDetailsService clientDetailsService = new JdbcClientDetailsService(dataSource);
if (passwordEncoder != null) {
// This is used to encode secrets as they are added to the database (if it isn't set then the user has top
// pass in pre-encoded secrets)
clientDetailsService.setPasswordEncoder(passwordEncoder);
}
for (ClientDetails client : clientDetails) {
try {
clientDetailsService.updateClientDetails(client);
} catch (NoSuchClientException e) {
clientDetailsService.addClientDetails(client);
}
}
return clientDetailsService;
}
这个方法,存在则更新client信息。
@Configuration
@EnableAuthorizationServer
@EnableConfigurationProperties
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private UserDetailsService userDetailsService;
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security.tokenKeyAccess("permitAll()").checkTokenAccess("isAuthenticated()")
.allowFormAuthenticationForClients();
}
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
// clients.jdbc(dataSource);
MyCustomJdbcClientDetailsServiceBuilder jdbc=new MyCustomJdbcClientDetailsServiceBuilder();
jdbc.dataSource(dataSource()).withClient("client")
.authorizedGrantTypes("client-credentials", "password", "refresh_token")
.authorities("ROLE_CLIENT", "ROLE_ANDROID_CLIENT").scopes("read", "write", "trust")
.resourceIds("oauth2-resource").accessTokenValiditySeconds(5000).secret("secret")
.refreshTokenValiditySeconds(50000);
clients.setBuilder(jdbc);
}
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager)
.allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST)
.tokenEnhancer(new CustomTokenEnhancer()).userDetailsService(userDetailsService);
}
@Bean
@ConfigurationProperties("spring.datasource")
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}
@Bean
public PasswordEncoder encoder() {
return new PasswordEncoder() {
@Override
public boolean matches(CharSequence rawPassword, String encodedPassword) {
return StringUtils.hasText(encodedPassword)
? MD5Utils.encoder(rawPassword.toString()).equals(encodedPassword)
: false;
}
@Override
public String encode(CharSequence rawPassword) {
return MD5Utils.encoder(rawPassword.toString());
}
};
}
}
4、UserDetailsService,实现loadUserByUsername 方法
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRep.loadUserByUsername(username);
return user;
}
5、向access_token添加额外信息 (非必要)
public class CustomTokenEnhancer implements TokenEnhancer {
@Override
public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
User user = (User) authentication.getPrincipal();
final Map<String, Object> additionalInfo = new HashMap<>();
additionalInfo.put("id", user.getId());
additionalInfo.put("name", user.getName());
additionalInfo.put("authorities", user.getAuthorities());
((DefaultOAuth2AccessToken) accessToken).setAdditionalInformation(additionalInfo);
return accessToken;
}
}
Token持久化建表语句:
create table if not exists oauth_client_details (
client_id VARCHAR(255) PRIMARY KEY,
resource_ids VARCHAR(255),
client_secret VARCHAR(255),
scope VARCHAR(255),
authorized_grant_types VARCHAR(255),
web_server_redirect_uri VARCHAR(255),
authorities VARCHAR(255),
access_token_validity INTEGER,
refresh_token_validity INTEGER,
additional_information VARCHAR(4096),
autoapprove VARCHAR(255)
);
create table if not exists oauth_client_token (
token_id VARCHAR(255),
token LONG VARBINARY,
authentication_id VARCHAR(255) PRIMARY KEY,
user_name VARCHAR(255),
client_id VARCHAR(255)
);
create table if not exists oauth_access_token (
token_id VARCHAR(255),
token LONG VARBINARY,
authentication_id VARCHAR(255) PRIMARY KEY,
user_name VARCHAR(255),
client_id VARCHAR(255),
authentication LONG VARBINARY,
refresh_token VARCHAR(255)
);
create table if not exists oauth_refresh_token (
token_id VARCHAR(255),
token LONG VARBINARY,
authentication LONG VARBINARY
);
create table if not exists oauth_code (
code VARCHAR(255), authentication LONG VARBINARY
);
create table if not exists oauth_approvals (
userId VARCHAR(255),
clientId VARCHAR(255),
scope VARCHAR(255),
status VARCHAR(10),
expiresAt TIMESTAMP,
lastModifiedAt TIMESTAMP
);
create table if not exists ClientDetails (
appId VARCHAR(255) PRIMARY KEY,
resourceIds VARCHAR(255),
appSecret VARCHAR(255),
scope VARCHAR(255),
grantTypes VARCHAR(255),
redirectUrl VARCHAR(255),
authorities VARCHAR(255),
access_token_validity INTEGER,
refresh_token_validity INTEGER,
additionalInformation VARCHAR(4096),
autoApproveScopes VARCHAR(255)
);
MyCustomJdbcClientDetailsServiceBuilder :
public class MyCustomJdbcClientDetailsServiceBuilder extends JdbcClientDetailsServiceBuilder {
private Set<ClientDetails> clientDetails = new HashSet<ClientDetails>();
private DataSource dataSource;
private PasswordEncoder passwordEncoder; // for writing client secrets
public JdbcClientDetailsServiceBuilder dataSource(DataSource dataSource) {
this.dataSource = dataSource;
return this;
}
public JdbcClientDetailsServiceBuilder passwordEncoder(PasswordEncoder passwordEncoder) {
this.passwordEncoder = passwordEncoder;
return this;
}
@Override
protected void addClient(String clientId, ClientDetails value) {
clientDetails.add(value);
}
@Override
protected ClientDetailsService performBuild() {
Assert.state(dataSource != null, "You need to provide a DataSource");
JdbcClientDetailsService clientDetailsService = new JdbcClientDetailsService(dataSource);
if (passwordEncoder != null) {
// This is used to encode secrets as they are added to the database (if it isn't set then the user has top
// pass in pre-encoded secrets)
clientDetailsService.setPasswordEncoder(passwordEncoder);
}
for (ClientDetails client : clientDetails) {
try {
clientDetailsService.updateClientDetails(client);
} catch (NoSuchClientException e) {
clientDetailsService.addClientDetails(client);
}
}
return clientDetailsService;
}
获取token:
访问未授权资源:
带上授权token,再次访问: