采用oauth2.0 + SpringSecurity 搭建一个oauth2.0授权服务中心和一些资源服务器(密码模式测试可用),实现简单的微服务接口安全和权限控制。
Spring-Security存在于各个微服务系统中实现对url的访问控制,oauth2实现对微服务所有的rest接口的权限控制。
这是一个资源服务器和授权中心分离的demo,token存到表中保存。
0 软件环境
1、jdk1.8
2、springboot 2.0.7.release
3、spring-security 5
4、spring-security-oauth2 2.0.7.RELEASE
5、jpa
1 需要了解的概念
1、client:客户端
2、resource owner : 用户(资源拥有者)
3、authorization center:授权中心服务器(获取token、验证token)
4、resource server : 资源服务器
2 授权服务端搭建
pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security.oauth.boot</groupId>
<artifactId>spring-security-oauth2-autoconfigure</artifactId>
<version>2.0.7.RELEASE</version>
</dependency>
SecurityConfig类:security的web安全适配器
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
//security配置: basic认证
http.httpBasic()
.and()
.authorizeRequests()
.anyRequest()
.authenticated();
}
/**
* 注册一种密码加密的bean,这里可以用自己的实现
* 不注册security报错
*/
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
/**
* 注册认证管理
* springboot2.0以后需要注册,不注册报错
*/
@Bean
@Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
}
OauthServerConfig类:oauth一些自定义细节的配置,虽然有默认的配置,但是默认的并不能满足我们大多数业务的需求。
@Configuration
@EnableAuthorizationServer
public class OauthServerConfig extends AuthorizationServerConfigurerAdapter {
@Autowired
private AuthenticationManager authenticationManager;
@Autowired
private UserDetailsService userDetailsService;
@Autowired
private DataSource dataSource;
@Bean // 声明TokenStore实现
public TokenStore tokenStore() {
return new JdbcTokenStore(dataSource);
}
/**
* token端点配置
*/
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
endpoints.authenticationManager(authenticationManager).userDetailsService(userDetailsService)
.tokenStore(tokenStore());
DefaultTokenServices tokenServices = new DefaultTokenServices();
tokenServices.setTokenStore(endpoints.getTokenStore());
tokenServices.setSupportRefreshToken(true);
tokenServices.setClientDetailsService(endpoints.getClientDetailsService());
tokenServices.setAccessTokenValiditySeconds( (int) TimeUnit.DAYS.toSeconds(1)); // 1天
endpoints.tokenServices(tokenServices);
}
/**
* 客户端细节配置
*/
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.jdbc(dataSource);
}
/**
* oauth的一些权限控制
*/
@Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) {
//允许表单认证
oauthServer.allowFormAuthenticationForClients();
//允许check_token访问
oauthServer.checkTokenAccess("permitAll()");
}
}
实体类:OauthAccessToken、OauthClientDetails、OauthRefreshToken,这三个实体类是建表用的(JPA规范),你也可以自己写sql建表,建完表后,要写一个clientId和clientSecret,用于你自己测试用~~
@Getter
@Setter
@Entity
public class OauthAccessToken {
@Column(length=256)
private String tokenId;
@Lob
@Basic(fetch = FetchType.LAZY)
@Column(name = "token", columnDefinition = "BLOB",nullable=true)
private String token;
@Id
@Column(length=250)
private String authenticationId;
@Column(length=256)
private String userName;
@Column(length=256)
private String clientId;
@Lob
@Basic(fetch = FetchType.LAZY)
@Column(name = "authentication", columnDefinition = "BLOB",nullable=true)
private String authentication;
@Column(length=256)
private String refreshToken;
}
@Getter
@Setter
@Entity
public class OauthClientDetails {
@Id
@Column(length=250)
private String clientId;
@Column(length=256)
private String resourceIds;
@Column(length=256)
private String clientSecret;
@Column(length=256)
private String scope;
@Column(length=256)
private String authorizedGrantTypes;
@Column(length=256)
private String webServerRedirectUri;
@Column(length=256)
private String authorities;
@Column(length=11)
private Integer accessTokenValidity;
@Column(length=11)
private Integer refreshTokenValidity;
@Column(length=4096)
private String additionalInformation;
@Column(length=256)
private String autoapprove;
}
@Getter
@Setter
@Entity
public class OauthRefreshToken {
@Id
@Column(length=250)
private String tokenId;
@Lob
@Basic(fetch = FetchType.LAZY)
@Column(name = "token", columnDefinition = "BLOB",nullable=true)
private String token;
@Lob
@Basic(fetch = FetchType.LAZY)
@Column(name = "authentication", columnDefinition = "BLOB",nullable=true)
private String authentication;
}
注意:client_secret也要加密保存,加密的方式就是你在security里配置的加密方式,明文的话,security会报错!!!
MyUserDetail类:该自定义类实现了Security的UserDetailsService接口,这个接口里有一堆关于用户可用性规则的方法。
public class MyUserDetail implements UserDetails {
//我们自定义的用户实体
private User user;
public MyUserDetail (User user){
this.user = user;
}
private static final long serialVersionUID = -5732157426896885480L;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
//暂时写死admin权限,可拿user的权限加进去
return AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_admin");
}
@Override
public String getPassword() {
return user.getPassword();
}
@Override
public String getUsername() {
return user.getAccount();
}
@Override
public boolean isAccountNonExpired() {
// 根据方法名字想这个方法是干嘛的,true就是放行,可自定义去实现
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}
MyUserDetailService类:该自定义类实现了security的UserDetailsService接口,里面就一个方法loadUserByUsername,就是说请求过来了,我们可以获取到请求中携带的用户名,然后我们重写成我们自己的验证即可(这一步也没必要,因为我们实现了UserDetail接口,把用户的一些信息塞到MyUserDetail的方法中,security就帮我做验证了)。
@Component
public class MyUserDetailService implements UserDetailsService{
/**
* 自己写的user服务层
*/
@Autowired
private UserService userServcie;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//根据用户名去数据表中获取user
User user = userServcie.getUserByAccount(username);
if(user == null){
throw new UsernameNotFoundException("");
}
return new MyUserDetail(user);
}
}
3 资源服务器端搭建
在另一个项目同样引入授权服务的pom相关。
加入OauthResourceServerConfig类:声明自己是一个资源服务器,并且所有请求都需要验证
@Configuration
@EnableResourceServer
public class OauthResourceServerConfig extends ResourceServerConfigurerAdapter {
@Override
public void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/**")
.authenticated();
}
}
在application.properties中加入配置:
#授权服务中心的ip和端口
security.oauth2.resource.token-info-uri=http://localhost:port/oauth/check_token
#clientId(表里存的)
security.oauth2.client.client-id=admin
#clinetSecret(表里存的)
security.oauth2.client.client-secret=admin
4 总结
项目要用oauth2.0+spring-security来管理接口的安全性和权限,组里也没人会,于是恶补了几天的知识。几点建议:
(1)学习oauth2.0前,一定要先学习一下spring-security的知识。
(2)了解oauth2.0 授权中心三个重要的配置:configure(ClientDetailsServiceConfigurer clients)、configure(AuthorizationServerSecurityConfigurer oauthServer)、configure(AuthorizationServerEndpointsConfigurer endpoints)
(3)security的两个接口的理解:UserDetails、UserDetailsService
(4)资源服务器如何和授权中心分离的,采用什么方式去验证token
原文链接:https://blog.csdn.net/u013189824/article/details/86540689