spring cloud 集成 oauth2 搭建认证服务器

1,104 阅读3分钟

又是折腾自己的一天,明明可以靠脸吃饭的我,偏偏选择了靠技术

image.png

废话不多说让我们开始进入正文吧

首先我们要先配置一下security

Security 配置

  • 导入maven依赖
<!--security oauth2安全依赖-->
<dependency>
   <groupId>org.springframework.cloud</groupId>
   <artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>
  • 自定义security配置类WebSecurityConfig继承WebSecurityConfigurerAdapter
  • 重写configure(HttpSecurity http)方法,定义拦截规则
  • 编写loginUser继承User
  • 编写LwUserDetailsService继承UserDetailsService,创建LwUserDetailsServiceImpl实现 LwUserDetailsService方法,用于加载用户信息以及用户资源权限
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {


    /**
     * 自定义鉴权规则
     * @param http
     */
    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.csrf().disable() //token方式 关闭csrf
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)//token 将session设置成无状态
                .and()
                .authorizeRequests()
                .antMatchers("/oauth/token").permitAll()  //登录接口放开鉴权
                .anyRequest().authenticated()
                .and()
                .cors();
    }

    /**
     * 将 AuthenticationManager 注入到spring容器中
     * AuthenticationManager默认是不对外开放得
     */
    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    /**
     * LwUserDetailsService继承UserDetailsService
     * 负责用户信息以及权限信息查询
     */
    @Bean
    public LwUserDetailsService userDetailsService() {
        return new LwUserDetailsServiceImpl();
    }

    /**
     * 设置密码加密方式
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
public class LoginUser extends User {

    @Getter
    @Setter
    private String userId;

    @Getter
    @Setter
    private String username;

    @Getter
    @Setter
    private String password;

    @Getter
    @Setter
    private String phone;

    @Getter
    @Setter
    private String avatar;

    @Getter
    @Setter
    private String openid;


    public LoginUser(String username, String password, String userId, String phone, String avatar, String openid, Collection<? extends GrantedAuthority> authorities) {
        super(username, password, authorities);
        this.username = username;
        this.userId = userId;
        this.password = password;
        this.phone = phone;
        this.avatar = avatar;
        this.openid = openid;
    }
}
public class LwUserDetailsServiceImpl implements LwUserDetailsService {

    @Resource
    private RemoteUserService remoteUserService;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        R<SysUser> sysUserR = remoteUserService.getUserInfo(username, SecurityConstants.FROM_IN);
        if (!sysUserR.isOk()) {
            throw new UsernameNotFoundException("账号或密码错误");
        }
        SysUser sysUser = sysUserR.getData();

        //TODO 获取资源权限注入到userdetails中----待完善
        Collection<? extends GrantedAuthority> authorities  = AuthorityUtils
                .createAuthorityList("admin");
        LoginUser user = new LoginUser(sysUser.getUsername(), sysUser.getPassword(),
                sysUser.getUserId(), sysUser.getPhone(), sysUser.getAvatar(), sysUser.getOpenid(), authorities);
        return user;
    }
}

以上主要是配置security的鉴权、自定义用户加载方式、密码加密方式以及开放AuthenticationManager对象

接下来我们要做的就是配置认证服务器

认证服务器配置

配置认证服务器我们需要做的事分为以下几点

  • 获取客户端信息,验证客户端是否有权限访问认证服务器,我们可以通过jdbc、内存等方式获取
  • 配置令牌服务,设置令牌存储策略
  • 配置令牌端点访问规则,以及配置令牌端点安全约束
/***
 *
 * 客户端配置
 * @param clients
 */
 //因为我这边数据库表名和默认标识不同,所以我需要自定义sql语句
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
    JdbcClientDetailsService jdbcClientDetailsService = new JdbcClientDetailsService(dataSource);
    jdbcClientDetailsService.setSelectClientDetailsSql(SecurityConstants.SELECT_CLIENT_DETAILS_SQL);
    clients.withClientDetails(jdbcClientDetailsService);
}


/**
 * 查询客户端sql
 */
String SELECT_CLIENT_DETAILS_SQL = "select client_id,client_secret, resource_ids, scope, "
        + "authorized_grant_types, web_server_redirect_uri, authorities, access_token_validity, "
        + "refresh_token_validity, additional_information, autoapprove"
        + " from sys_oauth_client_details where client_id = ?";
/**
 * 令牌服务
 *
 * @param
 */
@Bean
public AuthorizationServerTokenServices tokenServices() {
    DefaultTokenServices services = new DefaultTokenServices();
    services.setSupportRefreshToken(true);//启用刷新token
    services.setClientDetailsService(clientDetailsService); //自定义客户端加载方式
    services.setAuthenticationManager(authenticationManagerBean);//注入认证管理器,这样才可以兼容4中授权模式
    services.setTokenStore(tokenStore());//设置token存储策略


    //这一段因项目而异,我采用的是JWT token
    TokenEnhancerChain chain = new TokenEnhancerChain();
    chain.setTokenEnhancers(Arrays.asList(lwTokenEnhancer, jwtAccessTokenConverter));//采用JWTtoken方式增强
    services.setTokenEnhancer(chain);//设置token增强器
    return services;
}

/**
 * 存储策略
 */
@Bean
public TokenStore tokenStore() {
    return new JwtTokenStore(jwtAccessTokenConverter());
}

/**
 * jwt转换器
 */
@Bean
public JwtAccessTokenConverter jwtAccessTokenConverter() {
    DefaultAccessTokenConverter defaultConverter = new DefaultAccessTokenConverter();
    defaultConverter.setUserTokenConverter(lwUserAuthenticationConverter());

    JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
    converter.setAccessTokenConverter(defaultConverter);
    converter.setSigningKey("123456");
    return converter;
}
/**
 * 令牌访问端点配置
 *
 * @param endpoints
 */
@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
    endpoints.allowedTokenEndpointRequestMethods(HttpMethod.POST, HttpMethod.GET)
            .authenticationManager(authenticationManagerBean)
            .tokenServices(tokenServices())
            .exceptionTranslator(new LwWebResponseExceptionTranslator())
            .tokenStore(tokenStore())
            .userDetailsService(lwUserDetailsService);
}

/**
 * 端点约束
 *
 * @param security
 */
@Override
public void configure(AuthorizationServerSecurityConfigurer security) throws Exception {
    security.allowFormAuthenticationForClients().checkTokenAccess("isAuthenticated()");
}

不要忘记在配置类上加上@EnableAuthorizationServer注解

效果图

认证服务器默认情况下是不会返回用户信息的,也就说userInfo中的信息,是我通过往tokenservice中注入了lwTokenEnhancer增强方案,加入了userInfo后才返回的,具体怎么自定义返回信息可以看看spring cloud oauth 集成 JWT 之 token 增强 image.png