1. 添加组件依赖
在pom.xml
配置文件内添加如下:
<!--ApiBoot Security Oauth-->
<dependency>
<groupId>org.minbox.framework</groupId>
<artifactId>api-boot-starter-security-oauth-jwt</artifactId>
</dependency>
如果对
ApiBoot
使用不了解,可查看快速接入ApiBoot
2. ApiBoot Security
2.1 参数配置
配置名称 | 介绍 | 默认值 | 生效方式 |
---|---|---|---|
api.boot.security.away | SpringSecurity读取用户的方式,默认为内存方式 | memory | all |
api.boot.security.auth-prefix | 拦截的接口路径前缀,如:/api/users就会被默认拦截 | /api/ | memory/jdbc |
api.boot.security.users | 配置用户列表,具体使用查看内存方式介绍 | 无 | memory |
api.boot.security.ignoring-urls | Spring Security 所排除的路径,默认排除Swagger、Actuator相关路径前缀 | /v2/api-docs/swagger-ui.html/swagger-resources/configuration/security/META-INF/resources/webjars//swagger-resources/swagger-resources/configuration/ui/actuator/** | memory/jdbc |
api.boot.security.enable-default-store-delegate | 仅在Jdbc方式生效 | true | jdbc |
api.boot.security.disable-http-basic | 禁用basic http | true | memory/jdbc |
api.boot.security.disable-csrf | 禁用csrf | true | memory/jdbc |
2.2 配置内存用户
ApiBoot Security
可以直接使用内存的方式来设置使用用户列表,不集成数据库表结构形式使用,通过api.boot.security.users
配置参数进行设置,如下所示:
api:
boot:
security:
# Spring Security 内存方式用户列表示例
users:
- username: hengboy
password: 123456
roles: good
- username: apiboot
password: abc321
roles: user,order
username
:配置内存用户用户名password
:配置内存用户密码 (内部采用BCryptPasswordEncoder方式加密)roles
:配置内存用户角色列表,多个采用逗号隔开
2.3 配置JDBC用户
ApiBoot Security
当然是支持JDBC
自定义读取用户的方式,因为具体的业务可能是独立于用户认证中心的。
2.3.1 开启ApiBoot Security JDBC方式
通过api.boot.security.away=jdbc
参数配置来开启ApiBoot Security
的JDBC方式读取用户信息,该参数默认为memory(内存方式)
。
2.3.2 默认读取用户信息
ApiBoot Security
为了让使用者快速集成,内部默认支持了一个固定格式的表结构作为用户表,表名为api_boot_user_info
,默认的方式只需要创建表结构,然后进行维护表内的用户信息即可,结构SQL如下所示:
CREATE TABLE `api_boot_user_info` (
`UI_ID` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户编号,主键自增',
`UI_USER_NAME` varchar(30) DEFAULT NULL COMMENT '用户名',
`UI_NICK_NAME` varchar(50) DEFAULT NULL COMMENT '用户昵称',
`UI_PASSWORD` varchar(255) DEFAULT NULL COMMENT '用户密码',
`UI_EMAIL` varchar(30) DEFAULT NULL COMMENT '用户邮箱地址',
`UI_AGE` int(11) DEFAULT NULL COMMENT '用户年龄',
`UI_ADDRESS` varchar(200) DEFAULT NULL COMMENT '用户地址',
`UI_IS_LOCKED` char(1) DEFAULT 'N' COMMENT '是否锁定',
`UI_IS_ENABLED` char(1) DEFAULT 'Y' COMMENT '是否启用',
`UI_STATUS` char(1) DEFAULT 'O' COMMENT 'O:正常,D:已删除',
`UI_CREATE_TIME` timestamp NULL DEFAULT current_timestamp() COMMENT '用户创建时间',
PRIMARY KEY (`UI_ID`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='ApiBoot默认的用户信息表';
注意:
api_boot_user_info表内的密码字段必须是通过
BCryptPasswordEncoder
加密后的密文字符串。如果用户锁定了无法登录系统 (使用默认值即可)
如果用户未启用无法登录系统 (使用默认值即可)
2.3.3 自定义读取用户信息
在实际的应用中,一般都会有自己项目对应的用户信息,如果想让ApiBoot Security
读取自己用户表来进行认证登录,该怎么去做呢?
2.3.3.1 禁用默认读取用户方式
由于ApiBoot Security
内置了默认读取用户的方式,我们首先需要禁用掉它,可以通过api.boot.security.enable-default-store-delegate=false
参数配置禁用默认方式。
禁用默认读取用户方式后,我们需要来实现ApiBootStoreDelegate
接口来编写读取自己用户表内的数据。
2.3.3.2 自定义ApiBootStoreDelegate
实现ApiBootStoreDelegate
接口的实现类后需要让Spring IOC
进行托管,这样才可以生效,简单示例如下所示:
@Component
public class CustomUserStoreDelegate implements ApiBootStoreDelegate {
/**
* 返回根据username查询的用户详情对象
* UserDetails是SpringSecurity提供的用户详情接口
* 返回的自定义用户对象需实现UserDetails接口
* @param username 用户名
* @return
* @throws UsernameNotFoundException
*/
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
return null;
}
}
2.4 排除非拦截路径
2.4.1 默认排除路径
在ApiBootSecurityProperties
属性配置类内默认添加了的排除权限拦截的路径列表,如下所示:
/**
* 默认的排除路径列表
*/
public static final String[] DEFAULT_IGNORE_URLS = new String[]{
"/v2/api-docs",
"/swagger-ui.html",
"/swagger-resources/configuration/security",
"/META-INF/resources/webjars/**",
"/webjars/**",
"/swagger-resources",
"/swagger-resources/configuration/ui",
"/actuator/**"
};
可以看到ApiBoot Security
默认会将swagger
所需要的路径进行排除掉,这也是为什么通过ApiBoot Security Oauth
整合Swagger
时不再需要进行配置开放资源路径。
2.4.2 自定义排除路径
如果我们需要自定义排除路径可以通过api.boot.security.ignoreing-urls
参数进行配置,该参数使用数组方式接受值,多个使用逗号隔开或者使用集合形式配置,如下所示:
2.4.2.1 逗号隔开形式配置排除路径列表
api:
boot:
security:
ignoring-urls: /login,/register,/code/send
2.4.2.2 集合形式配置排除路径列表
api:
boot:
security:
ignoring-urls:
- /login
- /register
- /code/send
2.5 禁用HttpBasic
http basic
默认是被禁用的状态,如需开启,如下所示:
api:
boot:
security:
# 开启http basic
disable-http-basic: false
具体开启http basic后的注意事情,请查阅SpringSecurity相关文档。
2.6 禁用CSRF
csrf
默认是被禁用的状态,如需开启,如下所示:
api:
boot:
security:
# 开启csrf
disable-csrf: false
具体开启CSRF后的注意事情,请查阅SpringSecurity相关文档。
2.7 资源保护路径前缀
ApiBoot Security Oauth
默认保护的路径是/api/**
,该参数也是采用了数组的形式接收配置值,具体配置如下所示:
2.7.1 逗号隔开形式配置资源保护路径
api:
boot:
security:
auth-prefix: /user/**,/order/**
2.7.2 集合形式配置资源保护路径
api:
boot:
security:
auth-prefix:
- /user/**
- /order/**
3. ApiBoot Oauth2
3.1 参数配置
配置名称 | 介绍 | 默认值 | 绑定away |
---|---|---|---|
api.boot.oauth.away | Oauth存储Token、读取Client信息方式 | memory | all |
api.boot.oauth.cleint-id | Oauth2 Client ID | ApiBoot | memory |
api.boot.oauth.client-secret | Oauth2 Client Secret | ApiBootSecret | memory |
api.boot.oauth.resource-id | Oauth2接口资源编号 | api | memory |
api.boot.oauth.grant-types | 客户端授权方式 | Srtring[]{"password","refresh_token"} | memory |
api.boot.oauth.scopes | 客户端作用域 | String[]{"api"} | memory |
api.boot.oauth.jwt.enable | 是否启用JWT格式化AccessToken | false | memory/jdbc |
api.boot.oauth.jwt.sign-key | 使用JWT格式化AccessToken时的签名 | ApiBoot | memory/jdbc |
3.2 使用内存方式存储Token
ApiBoot Oauth
内部默认采用内存方式存储Token。
注意:由于Token存放在内存,项目重启后Token就会失效,需要重新获取。
3.2.1 自定义Client信息
ApiBoot Oauth
内默认了客户端的ClientId
、ClientSecret
两个配置参数的值(可参考上面的配置参数列表),我们可以通过如下方式进行修改:
api:
boot:
oauth:
# 设置ClientId
client-id: yuqiyu
# 设置ClientSecret
client-secret: hengboy@yuqiyu
修改后影响获取
access_token
时的客户端信息,具体使用方式请继续阅读文档。
3.3 使用JDBC方式存储Token
3.3.1 开启JDBC
由于ApiBoot Oauth
默认是内存方式,我们需要通过如下方式进行开启JDBC:
api:
boot:
oauth:
# 配置使用jdbc方式存储token、客户端信息等
away: jdbc
3.3.2 初始化表结构
jdbc
方式需要初始化Oauth2
提供的对应数据库表结构到项目使用数据库内,点击oauth-mysql.sql查看MySql
建表语句。
3.3.3 初始化Client信息
在初始化oauth2
数据库内执行如下sql
:
INSERT INTO oauth_client_details (client_id, resource_ids, client_secret, scope, authorized_grant_types, web_server_redirect_uri, authorities, access_token_validity, refresh_token_validity, additional_information, autoapprove) VALUES ('ApiBoot', 'api', '$2a$10$77BAX5ALfmT5UqDkprHaPOHYJ4we1noG7sJy.8hlj988dVthp5y36', 'api', 'password,refresh_token', null, null, null, null, null, null);
列描述如下:
client_id
:ClientIdresource_ids
:资源编号列表,多个逗号隔开client_secret
:ClientSecret,需要使用BCryptPasswordEncoder
进行加密。scope
:客户端授权范围authorized_grant_types
:授权类型,多个使用逗号隔开
3.4 获取AccessToken
3.4.1 Curl方式
在Mac
、Linux
系统下可以直接通过curl
命令行进行获取access_token
,命令如下所示:
~ curl ApiBoot:ApiBootSecret@localhost:8080/oauth/token -d "grant_type=password&username=apiboot&password=abc321"
获取结果:
{"access_token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsiYXBpIl0sInVzZXJfbmFtZSI6ImFwaWJvb3QiLCJzY29wZSI6WyJhcGkiXSwiZXhwIjoxNTYwNDQ2NDc5LCJhdXRob3JpdGllcyI6WyJST0xFX2FwaSJdLCJqdGkiOiI2ZmQ0ZDdiNi1kN2JkLTRiMmUtYmFlYi1iNGMwMmRlMjM0YmYiLCJjbGllbnRfaWQiOiJBcGlCb290In0.l_38N6gJbSug_uzJLope9uJQsA12BfJNDlGFmB-UQMU","token_type":"bearer","refresh_token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOlsiYXBpIl0sInVzZXJfbmFtZSI6ImFwaWJvb3QiLCJzY29wZSI6WyJhcGkiXSwiYXRpIjoiNmZkNGQ3YjYtZDdiZC00YjJlLWJhZWItYjRjMDJkZTIzNGJmIiwiZXhwIjoxNTYyOTk1Mjc5LCJhdXRob3JpdGllcyI6WyJST0xFX2FwaSJdLCJqdGkiOiIxNmZhZThlNi00ZDM3LTQ1NTctOTZiYi1hMWQ4MjBkOTk2NTYiLCJjbGllbnRfaWQiOiJBcGlCb290In0.egICzqsReO0hxheUv2i7u-3vloo7kYf1-_JqMcSR240","expires_in":42378,"scope":"api","jti":"6fd4d7b6-d7bd-4b2e-baeb-b4c02de234bf"}
curl
的命令组成部分,在@
符合之前是ClientId:ClientSecret
客户端信息,如果你并没有修改ApiBoot
默认提供的配置,这里就是ApiBoot:ApiBootSecret
,如果你需要修改客户端信息,内存方式,JDBC方式。
-d
表示请求的参数,ApiBoot Security Oauth
配置的用户列表是以password
授权方式进行访问使用,这里apiboot
为用户名,abc321
则为用户的密码。
3.4.2 PostMan方式
注意:
- 获取
access_token
的请求方式是POST
。- 使用
Basic
方式认证客户端信息- 不要混淆客户端的clientId、clientSecret与用户的username、password的概念。
3.4.3 RestTemplate方式
// 获取Token请求路径
String access_token_uri = "http://localhost:8080/oauth/token?grant_type=password&username=apiboot&password=abc321";
// 客户端Id
String clientId = "ApiBoot";
// 客户端Secret
String clientSecret = "ApiBootSecret";
// basic认证的格式
String basicAuth = "Basic %s";
// 可以使用注入RestTemplate方式获取对象实例
RestTemplate restTemplate = new RestTemplate();
// 请求头
HttpHeaders headers = new HttpHeaders();
// 设置客户端的basic认证信息
headers.set("Authorization", String.format(basicAuth, Base64Utils.encodeToString((clientId + ":" + clientSecret).getBytes())));
// 请求主体
HttpEntity<String> httpEntity = new HttpEntity<>(headers);
// 发送请求,获取access_token
String access_token = restTemplate.postForObject(access_token_uri, httpEntity, String.class);
System.out.println(access_token);
3.5 设置资源Id
3.5.1 内存方式设置
通过如下方式进行配置:
api:
boot:
oauth:
resource-id: hengboy_api
3.5.2 JDBC方式设置
修改oauth2
提供的oauth_client_details
表内数据的resource_ids
列值。
3.6 设置客户端可使用的GrantType
3.6.1 内存方式设置
ApiBoot Oauth
默认的授权方式为:password
、refresh_token
,如需修改通过以下方式进行配置:
api:
boot:
oauth:
grant-types:
- password
- refresh_token
- client_credentials
3.6.2 JDBC方式设置
修改oauth2
提供的oauth_client_details
表内数据的authorized_grant_types
列值。
3.7 自定义GrantType
Oauth2
内置了几种常用的授权方式,如:password
、refresh_token
、client_credentials
、code
等,在实际的业务场景中往往会出现特殊需求,比如:手机号验证码登录
、微信自动登录
、第三方账号登录
等,针对这种需求ApiBoot Security Oauth
提供了自定义GrantType
的方式来完成。
3.7.1 ApiBootOauthTokenGranter
ApiBootOauthTokenGranter
是由ApiBoot Security Oauth
提供的自定义GrantType
的接口,只需要实现该接口并把实现类交由Spring IOC
托管就可以完成自定义授权方式,下面我们来简单写一个短信验证码登录示例:
/**
* 短信验证码登录示例
*
* @author 恒宇少年 - 于起宇
* <p>
* DateTime:2019-06-06 09:15
* Blog:http://blog.yuqiyu.com
* WebSite:http://www.jianshu.com/u/092df3f77bca
* Gitee:https://gitee.com/hengboy
* GitHub:https://github.com/hengboy
*/
@Component
public class PhoneCodeOauthTokenGranter implements ApiBootOauthTokenGranter {
/**
* logger instance
*/
static Logger logger = LoggerFactory.getLogger(PhoneCodeOauthTokenGranter.class);
/**
* 获取Token时使用grant_type=phone_code授权方式
*/
private static final String GRANT_TYPE = "phone_code";
/**
* 参数:手机号
*/
private static final String PARAM_PHONE = "phone";
/**
* 参数:验证码
*/
private static final String PARAM_CODE = "code";
@Override
public String grantType() {
return GRANT_TYPE;
}
/**
* 该方法参数集合是获取Token时携带的参数
* 获取Token路径:/oauth/token?grant_type=phone_code&phone=171xxxxx&code=196523
* phone=171xxxxx
* code=196523
*
* @param parameters parameter map
* @return
* @throws ApiBootTokenException
*/
@Override
public UserDetails loadByParameter(Map<String, String> parameters) throws ApiBootTokenException {
String phone = parameters.get(PARAM_PHONE);
String code = parameters.get(PARAM_CODE);
logger.debug("手机号:{}", phone);
logger.debug("验证码:{}", code);
// 自定义数据逻辑校验验证码是否正确、是否与该手机号匹配等
// 校验通过后返回实现SpringSecurity提供的UserDetails接口的数据实体即可
return new UserDetails() {
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return null;
}
@Override
public String getPassword() {
return null;
}
@Override
public String getUsername() {
return phone;
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
};
}
}
3.7.2 获取自定义授权方式的Token
ApiBoot Security Oauth
修改了Oauth2
内部有关授权的源码方式进行实现,所以获取Token
跟普通没有区别,只不过是grant_type
参数有所变动,针对上面自定义短信验证码登录的授权方式获取Token
如下所示:
curl ApiBoot:ApiBootSecret@localhost:8080/oauth/token -d "grant_type=phone_code&phone=171xxxx&code=026492"
也可以通过
PostMan
、RestTemplate
方式获取Token
,注意自己项目的ClientId
、ClientSecret
的配置来替换ApiBoot:ApiBootSecret
默认值。
4. ApiBoot JWT
在ApiBoot Security Oauth
内默认集成了JWT
格式化Oauth Access Token
的转换方式,但是并未启用,需要通过api.boot.oauth.jwt.enable
来开启JWT
,如下所示:
api:
boot:
oauth:
away: jdbc
jwt:
# 开启Jwt转换AccessToken
enable: true
4.1 配置JWT秘钥
JWT
内部使用RSA
方式进行加密,加密时需要使用秘钥Key
,ApiBoot Security Oauth
内部默认使用ApiBoot
作为秘钥Key
,如果需要修改我们可以通过api.boot.oauth.jwt.sign-key
参数进行设置,如下所示:
api:
boot:
oauth:
away: jdbc
jwt:
# 转换Jwt时所需加密key,默认为ApiBoot
sign-key: 恒宇少年 - 于起宇