SpringSecurity + Oauth2.0搭建授权服务中心

       采用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

如有不对的理解,请指正,谢谢!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值