Spring Boot实现OAuth 2.0

忘记po源码了,点这里[github 源码]
开篇当然是包结构啦。
Spring Boot实现OAuth 2.0_第1张图片

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 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就行

你可能感兴趣的:(spring-boot,spring-security,oauth-2-0)