您的位置:首页 > 编程语言 > Java开发

Spring Boot实现OAuth 2.0

2018-01-03 15:10 423 查看
忘记po源码了,点这里[github 源码]

开篇当然是包结构啦。



1.在app入口,添加@EnableAuthorizationServer注解,声明这是个认证服务器

@EnableAuthorizationServer

@SpringBootApplication

【更正】

( scanBasePackages = {“com.qiyun.qy”}, exclude = {

DataSourceAutoConfiguration.class }) //由于是多数据源,所以,屏蔽默认数据源设置

这一段在spring boot 1.59版本不需要写。当你有了自己的数据源配置后,spring boot会自动采用你自定义的配置。

也正是由于我多加了这段exclude导致@ResponseBody无法正确返回。而我以为是缺少了jpa jar。

public class QyApplication {

public static void main(String[] args) {
SpringApplication.run(QyApplication.class, args);
}


}

2.继承WebSecurityConfigurerAdapter,实现用户认证

@Configuration

@EnableWebSecurity

public class SecurityConfig extends WebSecurityConfigurerAdapter {

@Autowired
@Qualifier("qiYunSecurityProvider")
private AuthenticationProvider provider;// 自定义验证

@Autowired
@Qualifier("qiYunUserService")
private UserDetailsService userService; //查询用户

@Override
protected void configure(HttpSecurity http) throws Exception {
http
.csrf().disable()//禁用csrf (csrf会拦截所有post请求)
.headers().frameOptions().disable()//允许使用iframe
.and().authorizeRequests()
.antMatchers("/home", "/register", "/socket").permitAll()//首页、注册、web socket,不需要权限
.antMatchers("/static/**", "/ueditor/**", "/error/**").permitAll()
.antMatchers("/oauth/**", "/api/**").permitAll()//当前filter不拦截 OAuth2.0 的路径
.anyRequest().authenticated()
.antMatchers("/admin/**").hasRole("admin")//预留管理员权限
.and().formLogin().defaultSuccessUrl("/home").permitAll()
.and().logout().logoutUrl("/login?logout").logoutSuccessUrl("/").permitAll();
}

@Autowired
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(provider);
}


}

2.1 验证用户信息

@Service(“qiYunSecurityProvider”)

public class SecurityProvider implements AuthenticationProvider {

@Autowired
@Qualifier("qiYunUserService")
private UserDetailsService userService;

@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {

String name = authentication.getName();
String rawPassword = authentication.getCredentials().toString();

User user = (User) userService.loadUserByUsername(name);
String sqlPassword = user.getPassword();

//加密
BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
if (!encoder.matches(rawPassword, sqlPassword)) {
throw new BadCredentialsException("用户名或密码错误");
}

List<GrantedAuthority> grantedAuths = user.getAuthorities();
return new UsernamePasswordAuthenticationToken(name, sqlPassword, grantedAuths);
}

@Override
public boolean supports(Class<?> authentication) {
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}


}

2.2 查询用户信息

@Service(“qiYunUserService”)

public class UserService implements UserDetailsService {

@Autowired
IUserDao userDao;

@Override
public User loadUserByUsername(String name)
throws UsernameNotFoundException {

User user = userDao.findByUserKey(name);
if (user == null) {
throw new UsernameNotFoundException("用户不存在!");
}

return user;
}


}

3.OAuth2 client认证

@Configuration

@EnableAuthorizationServer // 认证服务器注解

@EnableResourceServer //资源服务器注解

public class ServerConfig extends AuthorizationServerConfigurerAdapter {

@Autowired
@Qualifier("baseDataSource")
private DataSource dataSource;

@Autowired
@Qualifier("qiYunClientService")
private ClientDetailsService clientService;

@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
security
.tokenKeyAccess("permitAll()")
.checkTokenAccess("isAuthenticated()")
.allowFormAuthenticationForClients()//允许表单登录
.passwordEncoder(new BCryptPasswordEncoder());//client_secret的加密方式
}

@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.jdbc(dataSource).clients(clientService);

}

@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {

endpoints
.tokenStore(tokenStore());//使用内存中的 token store
//.authorizationCodeServices(
//new JdbcAuthorizationCodeServices(dataSource));
//使用Jdbctoken store
//.pathMapping("/oauth/token", "/oauth2/token");//自定义授权提交页面
}

@Bean
public TokenStore tokenStore(){
return new InMemoryTokenStore(); //使用内存中的 token store
//return new JdbcTokenStore(dataSource); ///使用Jdbctoken store
}


}

3.1 资源服务器配置

@Configuration

@EnableResourceServer

public class ResourceConfig extends ResourceServerConfigurerAdapter {

@Override
public void configure(HttpSecurity http) throws Exception {
http
.requestMatchers().antMatchers("/api/**")//仅拦截api路径,与用户认证隔离
.and().authorizeRequests()
.antMatchers(HttpMethod.GET, "/api/**").access("#oauth2.hasScope('read')")
.antMatchers(HttpMethod.POST, "/api/**").access("#oauth2.hasScope('write')");
}


}

3.1 客户端查询

@Service(“qiYunClientService”)

public class ClientService implements ClientDetailsService {

@Autowired
private IClientDao clientDao;

@Override
public ClientDetails loadClientByClientId(String clientId) throws ClientRegistrationException {
Client client = clientDao.loadClientByClientId(clientId);
return client;
}


}

至此,spring security的用户认证和OAuth2的客户端授权,已整合完成。

在这里点出几个坑:

1.不指定client_secret的加密方式,
那么spring boot会采用默认的BCryptPasswordEncoder()。
如果此时你数据库里的client_secret字段值未加密,
恭喜你,会报加密错误。
(ps:当时忙着搭框架,没截图。有兴趣的,可以自己尝试)

2.获取accesss_token时,不缺少jackson的相关jar包,
却报错 no converter。本以为是源码里的ResponseEntiry导致的。
后来测试@ResponseBody也报错,才发现是框架打错了。
检查了一遍pom,发现是spring-boot-starter-data-jpa干掉了。
添加上依赖,OK。
【更正】:不是因为缺少了jpa的jar包,而是因为app入口上的类排除导致的。

3.关于客户端授权链接“/oauth/authorize?
client_id=2&client_secret=secret&
response_type=code
&scope=read write trust
&redirect_uri=http://127.0.0.1/home”
看网上都是用逗号分割scope。自己用逗号总报错。
看完源码,发现源码采用的分隔符是空格。
所以,请求链接中也需要用空格分割。
至于数据库中,可以用逗号,
然后在自己的ClientDetails类的getScope()中,
将字符串转换成Set<String>就行
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息