概要

本文内容主要为spring cloud 授权服务的搭建,采用jwt认证。

GitHub 地址:https://github.com/fp2952/spring-cloud-base/tree/master/auth-center/auth-center-provider

添加依赖

Spring Security 及 Security 的OAuth2 扩展

        <dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-oauth2</artifactId>
</dependency>

启动类注解

启动类添加 @EnableAuthorizationServer 注解

@SpringCloudApplication
@EnableAuthorizationServer
@EnableFeignClients("com.peng.main.client")
public class AuthCenterProviderApplication {
public static void main(String[] args){
SpringApplication.run(AuthCenterProviderApplication.class, args);
}
}

Oauth2配置类AuthorizationServerConfigurerAdapter

AuthorizationServerConfigurerAdapter中:

  • ClientDetailsServiceConfigurer:用来配置客户端详情服务(ClientDetailsService),客户端详情信息在这里进行初始化,你能够把客户端详情信息写死在这里或者是通过数据库来存储调取详情信息。
  • AuthorizationServerSecurityConfigurer:用来配置令牌端点(Token Endpoint)的安全约束.
  • AuthorizationServerEndpointsConfigurer:用来配置授权(authorization)以及令牌(token)的访问端点和令牌服务(token services)。

    主要配置如下:

配置客户端详情信息(Client Details)

ClientDetailsServiceConfigurer (AuthorizationServerConfigurer 的一个回调配置项) 能够使用内存或者JDBC来实现客户端详情服务(ClientDetailsService),Spring Security OAuth2的配置方法是编写@Configuration类继承AuthorizationServerConfigurerAdapter,然后重写void configure(ClientDetailsServiceConfigurer clients)方法,如:

    @Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
// 使用JdbcClientDetailsService客户端详情服务
clients.withClientDetails(new JdbcClientDetailsService(dataSource));
}

这里使用Jdbc实现客户端详情服务,数据源dataSource不做叙述,使用框架默认的表,schema链接:

https://github.com/spring-projects/spring-security-oauth/blob/master/spring-security-oauth2/src/test/resources/schema.sql

配置令牌 管理 (jwtAccessTokenConverter)

JwtAccessTokenConverter是用来生成token的转换器,而token令牌默认是有签名的,且资源服务器需要验证这个签名。此处的加密及验签包括两种方式:

对称加密、非对称加密(公钥密钥)

对称加密需要授权服务器和资源服务器存储同一key值,而非对称加密可使用密钥加密,暴露公钥给资源服务器验签,本文中使用非对称加密方式,配置于AuthorizationServerConfigurerAdapter如下:

    @Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
endpoints.authenticationManager(authenticationManager)
// 配置JwtAccessToken转换器
.accessTokenConverter(jwtAccessTokenConverter())
// refresh_token需要userDetailsService
.reuseRefreshTokens(false).userDetailsService(userDetailsService);
//.tokenStore(getJdbcTokenStore());
} /**
* 使用非对称加密算法来对Token进行签名
* @return
*/
@Bean
public JwtAccessTokenConverter jwtAccessTokenConverter() { final JwtAccessTokenConverter converter = new JwtAccessToken();
// 导入证书
KeyStoreKeyFactory keyStoreKeyFactory =
new KeyStoreKeyFactory(new ClassPathResource("keystore.jks"), "mypass".toCharArray());
converter.setKeyPair(keyStoreKeyFactory.getKeyPair("mytest")); return converter;
}

通过 JDK 工具生成 JKS 证书文件,并将 keystore.jks 放入resource目录下

keytool -genkeypair -alias mytest -keyalg RSA -keypass mypass -keystore keystore.jks -storepass mypass

此处我们自定义JwtAccessToken用于添加额外用户信息

/**
* Created by fp295 on 2018/4/16.
* 自定义JwtAccessToken转换器
*/
public class JwtAccessToken extends JwtAccessTokenConverter { /**
* 生成token
* @param accessToken
* @param authentication
* @return
*/
@Override
public OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication) {
DefaultOAuth2AccessToken defaultOAuth2AccessToken = new DefaultOAuth2AccessToken(accessToken); // 设置额外用户信息
BaseUser baseUser = ((BaseUserDetail) authentication.getPrincipal()).getBaseUser();
baseUser.setPassword(null);
// 将用户信息添加到token额外信息中
defaultOAuth2AccessToken.getAdditionalInformation().put(Constant.USER_INFO, baseUser); return super.enhance(defaultOAuth2AccessToken, authentication);
} /**
* 解析token
* @param value
* @param map
* @return
*/
@Override
public OAuth2AccessToken extractAccessToken(String value, Map<String, ?> map){
OAuth2AccessToken oauth2AccessToken = super.extractAccessToken(value, map);
convertData(oauth2AccessToken, oauth2AccessToken.getAdditionalInformation());
return oauth2AccessToken;
} private void convertData(OAuth2AccessToken accessToken, Map<String, ?> map) {
accessToken.getAdditionalInformation().put(Constant.USER_INFO,convertUserData(map.get(Constant.USER_INFO))); } private BaseUser convertUserData(Object map) {
String json = JsonUtils.deserializer(map);
BaseUser user = JsonUtils.serializable(json, BaseUser.class);
return user;
}
}

JwtAccessToken 类中从authentication里的getPrincipal(实际为UserDetails接口)获取用户信息,所以我们需要实现自己的UserDetails

/**
* Created by fp295 on 2018/4/29.
* 包装org.springframework.security.core.userdetails.User类
*/
public class BaseUserDetail implements UserDetails, CredentialsContainer { private final BaseUser baseUser;
private final org.springframework.security.core.userdetails.User user; public BaseUserDetail(BaseUser baseUser, User user) {
this.baseUser = baseUser;
this.user = user;
} @Override
public void eraseCredentials() {
user.eraseCredentials();
} @Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return user.getAuthorities();
} @Override
public String getPassword() {
return user.getPassword();
} @Override
public String getUsername() {
return user.getUsername();
} @Override
public boolean isAccountNonExpired() {
return user.isAccountNonExpired();
} @Override
public boolean isAccountNonLocked() {
return user.isAccountNonLocked();
} @Override
public boolean isCredentialsNonExpired() {
return user.isCredentialsNonExpired();
} @Override
public boolean isEnabled() {
return user.isEnabled();
} public BaseUser getBaseUser() {
return baseUser;
}
}

授权端点开放

    @Override
public void configure(AuthorizationServerSecurityConfigurer oauthServer) {
oauthServer
// 开启/oauth/token_key验证端口无权限访问
.tokenKeyAccess("permitAll()")
// 开启/oauth/check_token验证端口认证权限访问
.checkTokenAccess("isAuthenticated()");
}

Security 配置

需要配置 DaoAuthenticationProvider、UserDetailService 等

@Configuration
@Order(ManagementServerProperties.ACCESS_OVERRIDE_ORDER)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter { // 自动注入UserDetailsService
@Autowired
private BaseUserDetailService baseUserDetailService; @Override
public void configure(HttpSecurity http) throws Exception {
http // 配置登陆页/login并允许访问
.formLogin().permitAll()
// 登出页
.and().logout().logoutUrl("/logout").logoutSuccessUrl("/")
// 其余所有请求全部需要鉴权认证
.and().authorizeRequests().anyRequest().authenticated()
// 由于使用的是JWT,我们这里不需要csrf
.and().csrf().disable();
} /**
* 用户验证
* @param auth
*/
@Override
public void configure(AuthenticationManagerBuilder auth) {
auth.authenticationProvider(daoAuthenticationProvider());
} @Bean
public DaoAuthenticationProvider daoAuthenticationProvider(){
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
// 设置userDetailsService
provider.setUserDetailsService(baseUserDetailService);
// 禁止隐藏用户未找到异常
provider.setHideUserNotFoundExceptions(false);
// 使用BCrypt进行密码的hash
provider.setPasswordEncoder(new BCryptPasswordEncoder(6));
return provider;
}
}

UserDetailsService 实现

@Service
public class BaseUserDetailService implements UserDetailsService { private Logger logger = LoggerFactory.getLogger(this.getClass()); @Autowired
private BaseUserService baseUserService;
@Autowired
private BaseRoleService baseRoleService; @Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { // 调用FeignClient查询用户
ResponseData<BaseUser> baseUserResponseData = baseUserService.getUserByUserName(username);
if(baseUserResponseData.getData() == null || !ResponseCode.SUCCESS.getCode().equals(baseUserResponseData.getCode())){
logger.error("找不到该用户,用户名:" + username);
throw new UsernameNotFoundException("找不到该用户,用户名:" + username);
}
BaseUser baseUser = baseUserResponseData.getData(); // 调用FeignClient查询角色
ResponseData<List<BaseRole>> baseRoleListResponseData = baseRoleService.getRoleByUserId(baseUser.getId());
List<BaseRole> roles;
if(baseRoleListResponseData.getData() == null || !ResponseCode.SUCCESS.getCode().equals(baseRoleListResponseData.getCode())){
logger.error("查询角色失败!");
roles = new ArrayList<>();
}else {
roles = baseRoleListResponseData.getData();
} // 获取用户权限列表
List<GrantedAuthority> authorities = new ArrayList();
roles.forEach(e -> {
// 存储用户、角色信息到GrantedAuthority,并放到GrantedAuthority列表
GrantedAuthority authority = new SimpleGrantedAuthority(e.getRoleCode());
authorities.add(authority); }); // 返回带有用户权限信息的User
org.springframework.security.core.userdetails.User user = new org.springframework.security.core.userdetails.User(baseUser.getUserName(),
baseUser.getPassword(), isActive(baseUser.getActive()), true, true, true, authorities);
return new BaseUserDetail(baseUser, user);
} private boolean isActive(int active){
return active == 1 ? true : false;
}
}

授权服务器验证

http://127.0.0.1:8080/oauth/authorize?client_id=clientId&response_type=code&redirect_uri=www.baidu.com

注意:client_id:为存储在数据库里的client_id, response_type:写死code

  1. 链接回车后进入spring security 的简单登陆页面,

  1. 输入账号密码,为实现的 UserDetailsService 要里获取的用户,点击 login,
  2. 进入简单授权页面,点击 Authorize,
  3. 重定向到 redirect_uri,并带有 code 参数:

    http://www.baidu.com?code=rTKETX
  4. post请求获取 token:

注意,此处需加 Authorization 请求头,值为 Basic xxx xxx 为 client_id:client_secret 的 base64编码。

Spring Cloud OAuth2(一) 搭建授权服务的更多相关文章

  1. spring cloud+dotnet core搭建微服务架构:Api授权认证(六)

    前言 这篇文章拖太久了,因为最近实在太忙了,加上这篇文章也非常长,所以花了不少时间,给大家说句抱歉.好,进入正题.目前的项目基本都是前后端分离了,前端分Web,Ios,Android...,后端也基本 ...

  2. spring cloud+.net core搭建微服务架构:Api授权认证(六)

    前言 这篇文章拖太久了,因为最近实在太忙了,加上这篇文章也非常长,所以花了不少时间,给大家说句抱歉.好,进入正题.目前的项目基本都是前后端分离了,前端分Web,Ios,Android...,后端也基本 ...

  3. vue+uni-app商城实战 | 第一篇:【有来小店】微信小程序快速开发接入Spring Cloud OAuth2认证中心完成授权登录

    一. 前言 本篇通过实战来讲述如何使用uni-app快速进行商城微信小程序的开发以及小程序如何接入后台Spring Cloud微服务. 有来商城 youlai-mall 项目是一套全栈商城系统,技术栈 ...

  4. 【译文】用Spring Cloud和Docker搭建微服务平台

    by Kenny Bastani Sunday, July 12, 2015 转自:http://www.kennybastani.com/2015/07/spring-cloud-docker-mi ...

  5. spring cloud+dotnet core搭建微服务架构:Api网关(三)

    前言 国庆假期,一直没有时间更新. 根据群里面的同学的提问,强烈推荐大家先熟悉下spring cloud.文章下面有纯洁大神的spring cloud系列. 上一章最后说了,因为服务是不对外暴露的,所 ...

  6. spring cloud+dotnet core搭建微服务架构:配置中心续(五)

    前言 上一章最后讲了,更新配置以后需要重启客户端才能生效,这在实际的场景中是不可取的.由于目前Steeltoe配置的重载只能由客户端发起,没有实现处理程序侦听服务器更改事件,所以还没办法实现彻底实现这 ...

  7. spring cloud+.net core搭建微服务架构:配置中心续(五)

    前言 上一章最后讲了,更新配置以后需要重启客户端才能生效,这在实际的场景中是不可取的.由于目前Steeltoe配置的重载只能由客户端发起,没有实现处理程序侦听服务器更改事件,所以还没办法实现彻底实现这 ...

  8. spring cloud+.net core搭建微服务架构:Api网关(三)

    前言 国庆假期,一直没有时间更新. 根据群里面的同学的提问,强烈推荐大家先熟悉下spring cloud.文章下面有纯洁大神的spring cloud系列. 上一章最后说了,因为服务是不对外暴露的,所 ...

  9. Spring Cloud和Docker搭建微服务平台

    用Spring Cloud和Docker搭建微服务平台 This blog series will introduce you to some of the foundational concepts ...

  10. 手把手教你使用spring cloud+dotnet core搭建微服务架构:服务治理(-)

    背景 公司去年开始使用dotnet core开发项目.公司的总体架构采用的是微服务,那时候由于对微服务的理解并不是太深,加上各种组件的不成熟,只是把项目的各个功能通过业务层面拆分,然后通过nginx代 ...

随机推荐

  1. Kotlin——初级篇(一):最详细的环境搭建

    众所周知,Kotlin出来已经良久了.Kotlin有着众多优势,不管是用于Android开发中,还是Java开发,都能缩减很大的代码量,大大提高了工作效率.而小生本人也是才从忙碌的个工作中抽身出来,有 ...

  2. 【BZOJ1787】[Ahoi2008]Meet 紧急集合 LCA

    [BZOJ1787][Ahoi2008]Meet 紧急集合 Description Input Output Sample Input 6 4 1 2 2 3 2 4 4 5 5 6 4 5 6 6 ...

  3. HTML5+CSS3 表格设计(Table)

    <style> body { width: 600px; margin: 40px auto; font-family: 'trebuchet MS', 'Lucida sans', Ar ...

  4. 手机相册管理(gallery) ---- HTML5+

    模块:gallery Gallery模块管理系统相册,支持从相册中选择图片或视频文件.保存图片或视频文件到相册等功能.通过plus.gallery获取相册管理对象. 管理我们手机上用到的相册:选择图片 ...

  5. R的替换sub和gsub

    sub(pattern, replacement, x, ignore.case = FALSE, perl = FALSE, fixed = FALSE, useBytes = FALSE) gsu ...

  6. 【转载】web开发中 web 容器的作用(如tomcat)

    我们讲到servlet可以理解服务器端处理数据的java小程序,那么谁来负责管理servlet呢?这时候我们就要用到web容器.它帮助我们管理着servlet等,使我们只需要将重心专注于业务逻辑. 什 ...

  7. Highway Project---zoj3946(最短路SPFA)

    题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=5718 题意: 有n个点 m(n,m<=10^5)条路,现在要建 ...

  8. yii2框架(一)安装

    手动安装下载yii2basic出现以下错误 2 修改 G:\learn\yii-basic-app-2.0.11\basic\config下web.conf 为cookieValidationKey设 ...

  9. MVC和观察者模式

    用观察者模式实现MVC框架! http://wenku.baidu.com/view/eff8bab069dc5022aaea0007.html 写的不错! Observer和ConcreteObse ...

  10. docker——libnetwork插件网络功能

    从1.7.0版本开始,Docker正是把网络和存储这两部分的功能都以插件化形式剥离出来,允许用户通过指令来选择不同的后端实现.剥离出来的独立容器网络项目叫libnetwork,从名字就能看出,它希望将 ...