1. 添加组件依赖

pom.xml配置文件内添加如下:

  1. <!--ApiBoot Security Oauth-->
  2. <dependency>
  3. <groupId>org.minbox.framework</groupId>
  4. <artifactId>api-boot-starter-security-oauth-jwt</artifactId>
  5. </dependency>

如果对ApiBoot使用不了解,可查看快速接入ApiBoot

2. ApiBoot Security

2.1 参数配置

配置名称介绍默认值生效方式
api.boot.security.awaySpringSecurity读取用户的方式,默认为内存方式memoryall
api.boot.security.auth-prefix拦截的接口路径前缀,如:/api/users就会被默认拦截/api/memory/jdbc
api.boot.security.users配置用户列表,具体使用查看内存方式介绍memory
api.boot.security.ignoring-urlsSpring 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方式生效truejdbc
api.boot.security.disable-http-basic禁用basic httptruememory/jdbc
api.boot.security.disable-csrf禁用csrftruememory/jdbc

2.2 配置内存用户

ApiBoot Security可以直接使用内存的方式来设置使用用户列表,不集成数据库表结构形式使用,通过api.boot.security.users配置参数进行设置,如下所示:

  1. api:
  2. boot:
  3. security:
  4. # Spring Security 内存方式用户列表示例
  5. users:
  6. - username: hengboy
  7. password: 123456
  8. roles: good
  9. - username: apiboot
  10. password: abc321
  11. 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如下所示:

  1. CREATE TABLE `api_boot_user_info` (
  2. `UI_ID` int(11) NOT NULL AUTO_INCREMENT COMMENT '用户编号,主键自增',
  3. `UI_USER_NAME` varchar(30) DEFAULT NULL COMMENT '用户名',
  4. `UI_NICK_NAME` varchar(50) DEFAULT NULL COMMENT '用户昵称',
  5. `UI_PASSWORD` varchar(255) DEFAULT NULL COMMENT '用户密码',
  6. `UI_EMAIL` varchar(30) DEFAULT NULL COMMENT '用户邮箱地址',
  7. `UI_AGE` int(11) DEFAULT NULL COMMENT '用户年龄',
  8. `UI_ADDRESS` varchar(200) DEFAULT NULL COMMENT '用户地址',
  9. `UI_IS_LOCKED` char(1) DEFAULT 'N' COMMENT '是否锁定',
  10. `UI_IS_ENABLED` char(1) DEFAULT 'Y' COMMENT '是否启用',
  11. `UI_STATUS` char(1) DEFAULT 'O' COMMENT 'O:正常,D:已删除',
  12. `UI_CREATE_TIME` timestamp NULL DEFAULT current_timestamp() COMMENT '用户创建时间',
  13. PRIMARY KEY (`UI_ID`)
  14. ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='ApiBoot默认的用户信息表';

注意:

  1. api_boot_user_info表内的密码字段必须是通过BCryptPasswordEncoder加密后的密文字符串。

  2. 如果用户锁定了无法登录系统 (使用默认值即可)

  3. 如果用户未启用无法登录系统 (使用默认值即可)

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进行托管,这样才可以生效,简单示例如下所示:

  1. @Component
  2. public class CustomUserStoreDelegate implements ApiBootStoreDelegate {
  3. /**
  4. * 返回根据username查询的用户详情对象
  5. * UserDetails是SpringSecurity提供的用户详情接口
  6. * 返回的自定义用户对象需实现UserDetails接口
  7. * @param username 用户名
  8. * @return
  9. * @throws UsernameNotFoundException
  10. */
  11. @Override
  12. public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
  13. return null;
  14. }
  15. }

2.4 排除非拦截路径

2.4.1 默认排除路径

ApiBootSecurityProperties属性配置类内默认添加了的排除权限拦截的路径列表,如下所示:

  1. /**
  2. * 默认的排除路径列表
  3. */
  4. public static final String[] DEFAULT_IGNORE_URLS = new String[]{
  5. "/v2/api-docs",
  6. "/swagger-ui.html",
  7. "/swagger-resources/configuration/security",
  8. "/META-INF/resources/webjars/**",
  9. "/webjars/**",
  10. "/swagger-resources",
  11. "/swagger-resources/configuration/ui",
  12. "/actuator/**"
  13. };

可以看到ApiBoot Security默认会将swagger所需要的路径进行排除掉,这也是为什么通过ApiBoot Security Oauth整合Swagger时不再需要进行配置开放资源路径。

2.4.2 自定义排除路径

如果我们需要自定义排除路径可以通过api.boot.security.ignoreing-urls参数进行配置,该参数使用数组方式接受值,多个使用逗号隔开或者使用集合形式配置,如下所示:

2.4.2.1 逗号隔开形式配置排除路径列表

  1. api:
  2. boot:
  3. security:
  4. ignoring-urls: /login,/register,/code/send

2.4.2.2 集合形式配置排除路径列表

  1. api:
  2. boot:
  3. security:
  4. ignoring-urls:
  5. - /login
  6. - /register
  7. - /code/send

2.5 禁用HttpBasic

http basic默认是被禁用的状态,如需开启,如下所示:

  1. api:
  2. boot:
  3. security:
  4. # 开启http basic
  5. disable-http-basic: false

具体开启http basic后的注意事情,请查阅SpringSecurity相关文档。

2.6 禁用CSRF

csrf默认是被禁用的状态,如需开启,如下所示:

  1. api:
  2. boot:
  3. security:
  4. # 开启csrf
  5. disable-csrf: false

具体开启CSRF后的注意事情,请查阅SpringSecurity相关文档。

2.7 资源保护路径前缀

ApiBoot Security Oauth默认保护的路径是/api/**,该参数也是采用了数组的形式接收配置值,具体配置如下所示:

2.7.1 逗号隔开形式配置资源保护路径

  1. api:
  2. boot:
  3. security:
  4. auth-prefix: /user/**,/order/**

2.7.2 集合形式配置资源保护路径

  1. api:
  2. boot:
  3. security:
  4. auth-prefix:
  5. - /user/**
  6. - /order/**

3. ApiBoot Oauth2

3.1 参数配置

配置名称介绍默认值绑定away
api.boot.oauth.awayOauth存储Token、读取Client信息方式memoryall
api.boot.oauth.cleint-idOauth2 Client IDApiBootmemory
api.boot.oauth.client-secretOauth2 Client SecretApiBootSecretmemory
api.boot.oauth.resource-idOauth2接口资源编号apimemory
api.boot.oauth.grant-types客户端授权方式Srtring[]{"password","refresh_token"}memory
api.boot.oauth.scopes客户端作用域String[]{"api"}memory
api.boot.oauth.jwt.enable是否启用JWT格式化AccessTokenfalsememory/jdbc
api.boot.oauth.jwt.sign-key使用JWT格式化AccessToken时的签名ApiBootmemory/jdbc

3.2 使用内存方式存储Token

ApiBoot Oauth内部默认采用内存方式存储Token。

注意:由于Token存放在内存,项目重启后Token就会失效,需要重新获取。

3.2.1 自定义Client信息

ApiBoot Oauth内默认了客户端的ClientIdClientSecret两个配置参数的值(可参考上面的配置参数列表),我们可以通过如下方式进行修改:

  1. api:
  2. boot:
  3. oauth:
  4. # 设置ClientId
  5. client-id: yuqiyu
  6. # 设置ClientSecret
  7. client-secret: hengboy@yuqiyu

修改后影响获取access_token时的客户端信息,具体使用方式请继续阅读文档。

3.3 使用JDBC方式存储Token

3.3.1 开启JDBC

由于ApiBoot Oauth默认是内存方式,我们需要通过如下方式进行开启JDBC:

  1. api:
  2. boot:
  3. oauth:
  4. # 配置使用jdbc方式存储token、客户端信息等
  5. away: jdbc

3.3.2 初始化表结构

jdbc方式需要初始化Oauth2提供的对应数据库表结构到项目使用数据库内,点击oauth-mysql.sql查看MySql建表语句。

3.3.3 初始化Client信息

在初始化oauth2数据库内执行如下sql

  1. 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:ClientId
  • resource_ids:资源编号列表,多个逗号隔开
  • client_secret:ClientSecret,需要使用BCryptPasswordEncoder进行加密。
  • scope:客户端授权范围
  • authorized_grant_types:授权类型,多个使用逗号隔开

3.4 获取AccessToken

3.4.1 Curl方式

MacLinux系统下可以直接通过curl命令行进行获取access_token,命令如下所示:

  1. ~ curl ApiBoot:ApiBootSecret@localhost:8080/oauth/token -d "grant_type=password&username=apiboot&password=abc321"
  2. 获取结果:
  3. {"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方式

http://apiboot.yuqiyu.com/img/access_token_postmain.png

注意:

  1. 获取access_token的请求方式是POST
  2. 使用Basic方式认证客户端信息
  3. 不要混淆客户端的clientId、clientSecret与用户的username、password的概念。

3.4.3 RestTemplate方式

  1. // 获取Token请求路径
  2. String access_token_uri = "http://localhost:8080/oauth/token?grant_type=password&username=apiboot&password=abc321";
  3. // 客户端Id
  4. String clientId = "ApiBoot";
  5. // 客户端Secret
  6. String clientSecret = "ApiBootSecret";
  7. // basic认证的格式
  8. String basicAuth = "Basic %s";
  9. // 可以使用注入RestTemplate方式获取对象实例
  10. RestTemplate restTemplate = new RestTemplate();
  11. // 请求头
  12. HttpHeaders headers = new HttpHeaders();
  13. // 设置客户端的basic认证信息
  14. headers.set("Authorization", String.format(basicAuth, Base64Utils.encodeToString((clientId + ":" + clientSecret).getBytes())));
  15. // 请求主体
  16. HttpEntity<String> httpEntity = new HttpEntity<>(headers);
  17. // 发送请求,获取access_token
  18. String access_token = restTemplate.postForObject(access_token_uri, httpEntity, String.class);
  19. System.out.println(access_token);

3.5 设置资源Id

3.5.1 内存方式设置

通过如下方式进行配置:

  1. api:
  2. boot:
  3. oauth:
  4. resource-id: hengboy_api

3.5.2 JDBC方式设置

修改oauth2提供的oauth_client_details表内数据的resource_ids列值。

3.6 设置客户端可使用的GrantType

3.6.1 内存方式设置

ApiBoot Oauth默认的授权方式为:passwordrefresh_token,如需修改通过以下方式进行配置:

  1. api:
  2. boot:
  3. oauth:
  4. grant-types:
  5. - password
  6. - refresh_token
  7. - client_credentials

3.6.2 JDBC方式设置

修改oauth2提供的oauth_client_details表内数据的authorized_grant_types列值。

3.7 自定义GrantType

Oauth2内置了几种常用的授权方式,如:passwordrefresh_tokenclient_credentialscode等,在实际的业务场景中往往会出现特殊需求,比如:手机号验证码登录微信自动登录第三方账号登录等,针对这种需求ApiBoot Security Oauth提供了自定义GrantType的方式来完成。

3.7.1 ApiBootOauthTokenGranter

ApiBootOauthTokenGranter是由ApiBoot Security Oauth提供的自定义GrantType的接口,只需要实现该接口并把实现类交由Spring IOC托管就可以完成自定义授权方式,下面我们来简单写一个短信验证码登录示例:

  1. /**
  2. * 短信验证码登录示例
  3. *
  4. * @author 恒宇少年 - 于起宇
  5. * <p>
  6. * DateTime:2019-06-06 09:15
  7. * Blog:http://blog.yuqiyu.com
  8. * WebSite:http://www.jianshu.com/u/092df3f77bca
  9. * Gitee:https://gitee.com/hengboy
  10. * GitHub:https://github.com/hengboy
  11. */
  12. @Component
  13. public class PhoneCodeOauthTokenGranter implements ApiBootOauthTokenGranter {
  14. /**
  15. * logger instance
  16. */
  17. static Logger logger = LoggerFactory.getLogger(PhoneCodeOauthTokenGranter.class);
  18. /**
  19. * 获取Token时使用grant_type=phone_code授权方式
  20. */
  21. private static final String GRANT_TYPE = "phone_code";
  22. /**
  23. * 参数:手机号
  24. */
  25. private static final String PARAM_PHONE = "phone";
  26. /**
  27. * 参数:验证码
  28. */
  29. private static final String PARAM_CODE = "code";
  30. @Override
  31. public String grantType() {
  32. return GRANT_TYPE;
  33. }
  34. /**
  35. * 该方法参数集合是获取Token时携带的参数
  36. * 获取Token路径:/oauth/token?grant_type=phone_code&phone=171xxxxx&code=196523
  37. * phone=171xxxxx
  38. * code=196523
  39. *
  40. * @param parameters parameter map
  41. * @return
  42. * @throws ApiBootTokenException
  43. */
  44. @Override
  45. public UserDetails loadByParameter(Map<String, String> parameters) throws ApiBootTokenException {
  46. String phone = parameters.get(PARAM_PHONE);
  47. String code = parameters.get(PARAM_CODE);
  48. logger.debug("手机号:{}", phone);
  49. logger.debug("验证码:{}", code);
  50. // 自定义数据逻辑校验验证码是否正确、是否与该手机号匹配等
  51. // 校验通过后返回实现SpringSecurity提供的UserDetails接口的数据实体即可
  52. return new UserDetails() {
  53. @Override
  54. public Collection<? extends GrantedAuthority> getAuthorities() {
  55. return null;
  56. }
  57. @Override
  58. public String getPassword() {
  59. return null;
  60. }
  61. @Override
  62. public String getUsername() {
  63. return phone;
  64. }
  65. @Override
  66. public boolean isAccountNonExpired() {
  67. return true;
  68. }
  69. @Override
  70. public boolean isAccountNonLocked() {
  71. return true;
  72. }
  73. @Override
  74. public boolean isCredentialsNonExpired() {
  75. return true;
  76. }
  77. @Override
  78. public boolean isEnabled() {
  79. return true;
  80. }
  81. };
  82. }
  83. }

3.7.2 获取自定义授权方式的Token

ApiBoot Security Oauth修改了Oauth2内部有关授权的源码方式进行实现,所以获取Token跟普通没有区别,只不过是grant_type参数有所变动,针对上面自定义短信验证码登录的授权方式获取Token如下所示:

  1. curl ApiBoot:ApiBootSecret@localhost:8080/oauth/token -d "grant_type=phone_code&phone=171xxxx&code=026492"

也可以通过PostManRestTemplate方式获取Token,注意自己项目的ClientIdClientSecret的配置来替换ApiBoot:ApiBootSecret默认值。

4. ApiBoot JWT

ApiBoot Security Oauth内默认集成了JWT格式化Oauth Access Token的转换方式,但是并未启用,需要通过api.boot.oauth.jwt.enable来开启JWT,如下所示:

  1. api:
  2. boot:
  3. oauth:
  4. away: jdbc
  5. jwt:
  6. # 开启Jwt转换AccessToken
  7. enable: true

4.1 配置JWT秘钥

JWT内部使用RSA方式进行加密,加密时需要使用秘钥KeyApiBoot Security Oauth内部默认使用ApiBoot作为秘钥Key,如果需要修改我们可以通过api.boot.oauth.jwt.sign-key参数进行设置,如下所示:

  1. api:
  2. boot:
  3. oauth:
  4. away: jdbc
  5. jwt:
  6. # 转换Jwt时所需加密key,默认为ApiBoot
  7. sign-key: 恒宇少年 - 于起宇