为什么使用spring-authorization-server?

前言 为什么使用spring-authorization-server? 真实原因:原先是因为个人原因,需要研究新版鉴权服务,看到了spring-authorization-server,使用过程中,想着能不能整合新版本cloud,因此此处先以springboot搭建spring-authorization-server,后续再替换为springcloud2021。
官方原因:原先使用spring security oauth,而该项目已经逐渐被淘汰,虽然网上还是有不少该方案,但秉着技术要随时代更新,从而使用spring-authorization-server
spring 团队正式宣布 spring security oauth 停止维护,该项目将不会再进行任何的迭代
项目构建 以springboot搭建spring-authorization-server(即认证与资源服务器) 数据库相关表结构构建 需要创建3张表,sql分别如下
create table `oauth2_authorization`  (  `id` varchar(100) character set utf8mb4 collate utf8mb4_unicode_ci not null,  `registered_client_id` varchar(100) character set utf8mb4 collate utf8mb4_unicode_ci not null,  `principal_name` varchar(200) character set utf8mb4 collate utf8mb4_unicode_ci not null,  `authorization_grant_type` varchar(100) character set utf8mb4 collate utf8mb4_unicode_ci not null,  `attributes` varchar(4000) character set utf8mb4 collate utf8mb4_unicode_ci null default null,  `state` varchar(500) character set utf8mb4 collate utf8mb4_unicode_ci null default null,  `authorization_code_value` blob null,  `authorization_code_issued_at` timestamp(0) null default null,  `authorization_code_expires_at` timestamp(0) null default null,  `authorization_code_metadata` varchar(2000) character set utf8mb4 collate utf8mb4_unicode_ci null default null,  `access_token_value` blob null,  `access_token_issued_at` timestamp(0) null default null,  `access_token_expires_at` timestamp(0) null default null,  `access_token_metadata` varchar(2000) character set utf8mb4 collate utf8mb4_unicode_ci null default null,  `access_token_type` varchar(100) character set utf8mb4 collate utf8mb4_unicode_ci null default null,  `access_token_scopes` varchar(1000) character set utf8mb4 collate utf8mb4_unicode_ci null default null,  `oidc_id_token_value` blob null,  `oidc_id_token_issued_at` timestamp(0) null default null,  `oidc_id_token_expires_at` timestamp(0) null default null,  `oidc_id_token_metadata` varchar(2000) character set utf8mb4 collate utf8mb4_unicode_ci null default null,  `refresh_token_value` blob null,  `refresh_token_issued_at` timestamp(0) null default null,  `refresh_token_expires_at` timestamp(0) null default null,  `refresh_token_metadata` varchar(2000) character set utf8mb4 collate utf8mb4_unicode_ci null default null,  primary key (`id`) using btree) engine = innodb character set = utf8mb4 collate = utf8mb4_unicode_ci row_format = dynamic;  create table `oauth2_authorization_consent`  (  `registered_client_id` varchar(100) character set utf8mb4 collate utf8mb4_unicode_ci not null,  `principal_name` varchar(200) character set utf8mb4 collate utf8mb4_unicode_ci not null,  `authorities` varchar(1000) character set utf8mb4 collate utf8mb4_unicode_ci not null,  primary key (`registered_client_id`, `principal_name`) using btree) engine = innodb character set = utf8mb4 collate = utf8mb4_unicode_ci row_format = dynamic;   create table `oauth2_registered_client`  (  `id` varchar(100) character set utf8mb4 collate utf8mb4_unicode_ci not null,  `client_id` varchar(100) character set utf8mb4 collate utf8mb4_unicode_ci not null,  `client_id_issued_at` timestamp(0) not null default current_timestamp(0),  `client_secret` varchar(200) character set utf8mb4 collate utf8mb4_unicode_ci null default null,  `client_secret_expires_at` timestamp(0) null default null,  `client_name` varchar(200) character set utf8mb4 collate utf8mb4_unicode_ci not null,  `client_authentication_methods` varchar(1000) character set utf8mb4 collate utf8mb4_unicode_ci not null,  `authorization_grant_types` varchar(1000) character set utf8mb4 collate utf8mb4_unicode_ci not null,  `redirect_uris` varchar(1000) character set utf8mb4 collate utf8mb4_unicode_ci null default null,  `scopes` varchar(1000) character set utf8mb4 collate utf8mb4_unicode_ci not null,  `client_settings` varchar(2000) character set utf8mb4 collate utf8mb4_unicode_ci not null,  `token_settings` varchar(2000) character set utf8mb4 collate utf8mb4_unicode_ci not null,  primary key (`id`) using btree) engine = innodb character set = utf8mb4 collate = utf8mb4_unicode_ci row_format = dynamic; 先进行认证服务器相关配置 pom.xml引入依赖
注意!!!spring boot版本需2.6.x以上,是为后面升级成cloud做准备
    org.projectlombok    lombok    1.18.22     com.xxxx.iov    iov-cloud-framework-web    2.0.0-snapshot                                org.springframework.boot            spring-boot-starter-web                 org.springframework.boot    spring-boot-starter-web    2.6.6     cn.hutool    hutool-all    5.8.0     com.alibaba    fastjson    1.2.39     org.springframework.boot    spring-boot-starter-security     org.springframework.security    spring-security-oauth2-authorization-server    0.2.3     org.springframework.security    spring-security-cas     org.springframework.boot    spring-boot-starter-thymeleaf     com.alibaba    druid-spring-boot-starter    1.2.9     mysql    mysql-connector-java    8.0.28     com.baomidou    mybatis-plus-boot-starter    3.5.1     com.google.guava    guava    31.1-jre 创建自定义登录页面 login.html (可不要,使用自带的登录界面)
                    login page                                sign in to start your session
                            invalid username and password.            
                            you have been logged out.
sign in
i forgot my password            
                            register a new membership
创建自定义授权页面 consent.html(可不要,可使用自带的授权页面)
                授权页面                   用户授权确认    
                                        应用                                想要访问您的账号
上述应用程序请求以下权限
请审阅以下选项并勾选您同意的权限
您已对上述应用授予以下权限:
同意授权                                    
                                                            取消授权
需要您同意并提供访问权限。                    
如果您不同意,请单击取消授权,将不会为上述应用程序提供任何您的信息。
修改配置文件 application.yml(配置内容可自行简略)
server:  port: 9000 spring:  application:    name: authorization-server  thymeleaf:    cache: false  datasource:    url: jdbc//192.168.1.69:3306/test    username: root    password: root    driver-class-name: com.mysql.cj.jdbc.driver  security:    oauth2:      resourceserver:        jwt:          issuer-uri: http://127.0.0.1:9000  #认证中心端点,作为资源端的配置          application:  security:    excludeurls: #excludeurls中存放白名单地址      - /favicon.ico  # mybatis plus配置mybatis-plus:  mapper-locations: classpath:/mapper/*mapper.xml  global-config:    # 关闭mp3.0自带的banner    banner: false    db-config:      #主键类型  0:数据库id自增, 1:不操作, 2:用户输入id,3:数字型snowflake, 4:全局唯一id uuid, 5:字符串型snowflake;      id-type: auto      #字段策略      insert-strategy: not_null      update-strategy: not_null      select-strategy: not_null      #驼峰下划线w转换      table-underline: true      # 逻辑删除配置      # 逻辑删除全局值(1表示已删除,这也是mybatis plus的默认配置)      logic-delete-value: 1      # 逻辑未删除全局值(0表示未删除,这也是mybatis plus的默认配置)      logic-not-delete-value: 0  configuration:    #驼峰    map-underscore-to-camel-case: true    #打开二级缓存    cache-enabled: true    # log-impl: org.apache.ibatis.logging.stdout.stdoutimpl #开启sql日志 新增认证服务器配置文件 authorizationserverconfig
@configuration(proxybeanmethods = false)public class authorizationserverconfig {    /**     * 自定义授权页面     * 使用系统自带的即不用     */    private static final string custom_consent_page_uri = /oauth2/consent;     /**     * 自定义userdetailsservice     */    @autowired    private userservice userservice;      /**     *     * 使用默认配置进行form表单登录     * oauth2authorizationserverconfiguration.applydefaultsecurity(http)     */    @bean    @order(ordered.highest_precedence)    public securityfilterchain authorizationserversecurityfilterchain(httpsecurity http) throws exception {        oauth2authorizationserverconfigurer authorizationserverconfigurer = new oauth2authorizationserverconfigurer();         authorizationserverconfigurer.authorizationendpoint(authorizationendpoint -> authorizationendpoint.consentpage(custom_consent_page_uri));         requestmatcher endpointsmatcher = authorizationserverconfigurer.getendpointsmatcher();         http                .requestmatcher(endpointsmatcher)                .userdetailsservice(userservice)                .authorizerequests(authorizerequests -> authorizerequests.anyrequest().authenticated())                .csrf(csrf -> csrf.ignoringrequestmatchers(endpointsmatcher))                .apply(authorizationserverconfigurer);        return http.formlogin(customizer.withdefaults()).build();    }     /**     * 注册客户端应用     */    @bean    public registeredclientrepository registeredclientrepository(jdbctemplate jdbctemplate) {        // save registered client in db as if in-jdbc        registeredclient registeredclient = registeredclient.withid(uuid.randomuuid().tostring())                .clientid(zxg)                .clientsecret(123)                .clientauthenticationmethod(clientauthenticationmethod.client_secret_basic)                .authorizationgranttype(authorizationgranttype.authorization_code)                .authorizationgranttype(authorizationgranttype.refresh_token)                .authorizationgranttype(authorizationgranttype.client_credentials)                // 回调地址                .redirecturi(http://www.baidu.com)                // scope自定义的客户端范围                .scope(oidcscopes.openid)                .scope(message.read)                .scope(message.write)                // client请求访问时需要授权同意                .clientsettings(clientsettings.builder().requireauthorizationconsent(true).build())                // token配置项信息                .tokensettings(tokensettings.builder()                        // token有效期100分钟                        .accesstokentimetolive(duration.ofminutes(100l))                        // 使用默认jwt相关格式                        .accesstokenformat(oauth2tokenformat.self_contained)                        // 开启刷新token                        .reuserefreshtokens(true)                        // refreshtoken有效期120分钟                        .refreshtokentimetolive(duration.ofminutes(120l))                        .idtokensignaturealgorithm(signaturealgorithm.rs256).build()                )                .build();         // save registered client in db as if in-memory        jdbcregisteredclientrepository registeredclientrepository = new jdbcregisteredclientrepository(jdbctemplate);        registeredclientrepository.save(registeredclient);        return registeredclientrepository;    }     /**     * 授权服务:管理oauth2授权信息服务     */    @bean    public oauth2authorizationservice authorizationservice(jdbctemplate jdbctemplate, registeredclientrepository registeredclientrepository) {        return new jdbcoauth2authorizationservice(jdbctemplate, registeredclientrepository);    }     /**     * 授权确认信息处理服务     */    @bean    public oauth2authorizationconsentservice authorizationconsentservice(jdbctemplate jdbctemplate, registeredclientrepository registeredclientrepository) {        return new jdbcoauth2authorizationconsentservice(jdbctemplate, registeredclientrepository);    }     /**     * 加载jwk资源     * jwt:指的是 json web token,不存在签名的jwt是不安全的,存在签名的jwt是不可窜改的     * jws:指的是签过名的jwt,即拥有签名的jwt     * jwk:既然涉及到签名,就涉及到签名算法,对称加密还是非对称加密,那么就需要加密的 密钥或者公私钥对。此处我们将 jwt的密钥或者公私钥对统一称为 json web key,即 jwk。     */    @bean    public jwksource jwksource() {        rsakey rsakey = jwksutils.generatersa();        jwkset jwkset = new jwkset(rsakey);        return (jwkselector, securitycontext) -> jwkselector.select(jwkset);    }     /**     * 配置 oauth2.0 提供者元信息     */    @bean    public providersettings providersettings() {        return providersettings.builder().issuer(http://127.0.0.1:9000).build();    } } 新增security的配置文件websecurityconfig
@configuration@enablewebsecurity(debug = true) //开启securitypublic class websecurityconfig {    @autowired    private applicationproperties properties;     /**     * 设置加密方式     */    @bean    public passwordencoder passwordencoder() {//        // 将密码加密方式采用委托方式,默认以bcryptpasswordencoder方式进行加密,兼容ldap,md4,md5等方式//        return passwordencoderfactories.createdelegatingpasswordencoder();         // 此处我们使用明文方式 不建议这样        return nooppasswordencoder.getinstance();    }     /**     * 使用websecurity.ignoring()忽略某些url请求,这些请求将被spring security忽略     */    @bean    websecuritycustomizer websecuritycustomizer() {        return new websecuritycustomizer() {            @override            public void customize(websecurity web) {                // 读取配置文件application.security.excludeurls下的链接进行忽略                web.ignoring().antmatchers(properties.getsecurity().getexcludeurls().toarray(new string[]{}));            }        };    }     /**     * 针对http请求,进行拦截过滤     *     * cookiecsrftokenrepository进行csrf保护的工作方式:     *      1.客户端向服务器发出get请求,例如请求主页     *      2.spring发送 get 请求的响应以及 set-cookie 标头,其中包含安全生成的xsrf令牌     */    @bean    public securityfilterchain httpsecurityfilterchain(httpsecurity httpsecurity) throws exception {        httpsecurity                .authorizerequests(authorizerequests ->                        authorizerequests.antmatchers(/login).permitall()                                .anyrequest().authenticated()                )                 //使用默认登录页面                //.formlogin(withdefaults())                 //设置form登录,设置且放开登录页login                .formlogin(fromlogin -> fromlogin.loginpage(/login).permitall())                 // spring security csrf保护                .csrf(csrftoken -> csrftoken.csrftokenrepository(new cookiecsrftokenrepository()))                //                 //开启认证服务器的资源服务器相关功能,即需校验token//                .oauth2resourceserver()//                .accessdeniedhandler(new simpleaccessdeniedhandler())//                .authenticationentrypoint(new simpleauthenticationentrypoint())//                .jwt()        ;        return httpsecurity.build();    } } 新增读取application配置的类 applicationproperties
/*** 此步主要是获取配置文件中配置的白名单,可自行舍去或自定义实现其他方式**/@data@component@configurationproperties(application)public class applicationproperties {    private final security security = new security();     @data    public static class security {        private oauth2 oauth2;        private list excludeurls = new arraylist();         @data        public static class oauth2 {            private string issuerurl;         }    }} 新增 jwksutils 类和 keygeneratorutils,这两个类作为jwt对称加密
public final class jwksutils {     private jwksutils() {    }     /**     * 生成rsa加密key (即jwk)     */    public static rsakey generatersa() {        // 生成rsa加密的key        keypair keypair = keygeneratorutils.generatersakey();        // 公钥        rsapublickey publickey = (rsapublickey) keypair.getpublic();        // 私钥        rsaprivatekey privatekey = (rsaprivatekey) keypair.getprivate();        // 构建rsa加密key        return new rsakey.builder(publickey)                .privatekey(privatekey)                .keyid(uuid.randomuuid().tostring())                .build();    }     /**     * 生成ec加密key (即jwk)     */    public static eckey generateec() {        // 生成ec加密的key        keypair keypair = keygeneratorutils.generateeckey();        // 公钥        ecpublickey publickey = (ecpublickey) keypair.getpublic();        // 私钥        ecprivatekey privatekey = (ecprivatekey) keypair.getprivate();        // 根据公钥参数生成曲线        curve curve = curve.forecparameterspec(publickey.getparams());        // 构建ec加密key        return new eckey.builder(curve, publickey)                .privatekey(privatekey)                .keyid(uuid.randomuuid().tostring())                .build();    }     /**     * 生成hmacsha256密钥     */    public static octetsequencekey generatesecret() {        secretkey secretkey = keygeneratorutils.generatesecretkey();        return new octetsequencekey.builder(secretkey)                .keyid(uuid.randomuuid().tostring())                .build();    }}  class keygeneratorutils {     private keygeneratorutils() {    }     /**     * 生成rsa密钥     */    static keypair generatersakey() {        keypair keypair;        try {            keypairgenerator keypairgenerator = keypairgenerator.getinstance(rsa);            keypairgenerator.initialize(2048);            keypair = keypairgenerator.generatekeypair();        } catch (exception ex) {            throw new illegalstateexception(ex);        }        return keypair;    }     /**     * 生成ec密钥     */    static keypair generateeckey() {        ellipticcurve ellipticcurve = new ellipticcurve(                new ecfieldfp(                        new biginteger(115792089210356248762697446949407573530086143415290314195533631308867097853951)),                new biginteger(115792089210356248762697446949407573530086143415290314195533631308867097853948),                new biginteger(41058363725152142129326129780047268409114441015993725554835256314039467401291));        ecpoint ecpoint = new ecpoint(                new biginteger(48439561293906451759052585252797914202762949526041747995844080717082404635286),                new biginteger(36134250956749795798585127919587881956611106672985015071877198253568414405109));        ecparameterspec ecparameterspec = new ecparameterspec(                ellipticcurve,                ecpoint,                new biginteger(115792089210356248762697446949407573529996955224135760342422259061068512044369),                1);         keypair keypair;        try {            keypairgenerator keypairgenerator = keypairgenerator.getinstance(ec);            keypairgenerator.initialize(ecparameterspec);            keypair = keypairgenerator.generatekeypair();        } catch (exception ex) {            throw new illegalstateexception(ex);        }        return keypair;    }     /**     * 生成hmacsha256密钥     */    static secretkey generatesecretkey() {        secretkey hmackey;        try {            hmackey = keygenerator.getinstance(hmacsha256).generatekey();        } catch (exception ex) {            throw new illegalstateexception(ex);        }        return hmackey;    }} 新建 consentcontroller,编写登录和认证页面的跳转
如果在上面没有使用自定义的登录和授权页面,下面的跳转方法按需舍去
@slf4j@controllerpublic class consentcontroller {     private final registeredclientrepository registeredclientrepository;    private final oauth2authorizationconsentservice authorizationconsentservice;     public consentcontroller(registeredclientrepository registeredclientrepository,                             oauth2authorizationconsentservice authorizationconsentservice) {        this.registeredclientrepository = registeredclientrepository;        this.authorizationconsentservice = authorizationconsentservice;    }     @responsebody    @getmapping(/favicon.ico)    public string faviconico(){        return favicon.ico;    }     @getmapping(/login)    public string loginpage(){        return login;    }     @getmapping(value = /oauth2/consent)    public string consent(principal principal, model model,                          @requestparam(oauth2parameternames.client_id) string clientid,                          @requestparam(oauth2parameternames.scope) string scope,                          @requestparam(oauth2parameternames.state) string state) {         // remove scopes that were already approved        set scopestoapprove = new hashset();        set previouslyapprovedscopes = new hashset();        registeredclient registeredclient = this.registeredclientrepository.findbyclientid(clientid);        oauth2authorizationconsent currentauthorizationconsent =                this.authorizationconsentservice.findbyid(registeredclient.getid(), principal.getname());        set authorizedscopes;        if (currentauthorizationconsent != null) {            authorizedscopes = currentauthorizationconsent.getscopes();        } else {            authorizedscopes = collections.emptyset();        }        for (string requestedscope : stringutils.delimitedlisttostringarray(scope,  )) {            if (authorizedscopes.contains(requestedscope)) {                previouslyapprovedscopes.add(requestedscope);            } else {                scopestoapprove.add(requestedscope);            }        }         model.addattribute(clientid, clientid);        model.addattribute(state, state);        model.addattribute(scopes, withdescription(scopestoapprove));        model.addattribute(previouslyapprovedscopes, withdescription(previouslyapprovedscopes));        model.addattribute(principalname, principal.getname());         return consent;    }     private static set withdescription(set scopes) {        set scopewithdescriptions = new hashset();        for (string scope : scopes) {            scopewithdescriptions.add(new scopewithdescription(scope));         }        return scopewithdescriptions;    }     public static class scopewithdescription {        private static final string default_description = unknown scope - we cannot provide information about this permission, use caution when granting this.;        private static final map scopedescriptions = new hashmap();        static {            scopedescriptions.put(                    message.read,                    this application will be able to read your message.            );            scopedescriptions.put(                    message.write,                    this application will be able to add new messages. it will also be able to edit and delete existing messages.            );            scopedescriptions.put(                    other.scope,                    this is another scope example of a scope description.            );        }         public final string scope;        public final string description;         scopewithdescription(string scope) {            this.scope = scope;            this.description = scopedescriptions.getordefault(scope, default_description);        }    } } 新建 usercontroller,user,userservice等标准的自定义用户业务,此处仅放出userserviceimpl
@requiredargsconstructor@slf4j@componentclass userserviceimpl implements userservice {    private final usermapper usermapper;     @override    public userdetails loaduserbyusername(string username) throws usernamenotfoundexception {        user user = usermapper.selectone(new lambdaquerywrapper().eq(user::getusername,username));        return new org.springframework.security.core.userdetails.user(username, user.getpassword(), new arraylist());    }} 启动项目,如下图
认证服务器整体结构图
资源服务器相关配置 pom.xml引入资源服务器相关依赖
    org.springframework.boot    spring-boot-starter-oauth2-resource-server     org.springframework.boot    spring-boot-starter-security 新增配置文件 application.yaml
server:  port: 9003spring:  application:    name: resource  security:    oauth2:      resourceserver:        jwt:          issuer-uri: http://127.0.0.1:9000feign:  client:    config:      default: #配置超时时间        connect-timeout: 10000        read-timeout: 10000 新增资源服务器配置文件 resourceserverconfiguration
@configuration@enablewebsecurity(debug = true)@enableglobalmethodsecurity(prepostenabled = true) //开启鉴权服务public class resourceserverconfiguration {     @bean    public securityfilterchain httpsecurityfilterchain(httpsecurity httpsecurity) throws exception {        // 所有请求都进行拦截        httpsecurity.authorizerequests().anyrequest().authenticated();        // 关闭session        httpsecurity.sessionmanagement().disable();        // 配置资源服务器的无权限,无认证拦截器等 以及jwt验证        httpsecurity.oauth2resourceserver()                .accessdeniedhandler(new simpleaccessdeniedhandler())                .authenticationentrypoint(new simpleauthenticationentrypoint())                .jwt();        return httpsecurity.build();    } } 新增相关无认证无权限统一拦截回复 simpleaccessdeniedhandler 和 simpleauthenticationentrypoint
/** * 携带了token 而且token合法 但是权限不足以访问其请求的资源 403 * @author zxg */public class simpleaccessdeniedhandler implements accessdeniedhandler {     @override    public void handle(httpservletrequest request, httpservletresponse response, accessdeniedexception accessdeniedexception) throws ioexception, servletexception {        response.setstatus(httpservletresponse.sc_forbidden);        response.setcharacterencoding(utf-8);        response.setcontenttype(mediatype.application_json_value);        objectmapper objectmapper = new objectmapper();        string resbody = objectmapper.writevalueasstring(singleresultbundle.failed(无权访问));        printwriter printwriter = response.getwriter();        printwriter.print(resbody);        printwriter.flush();        printwriter.close();    }}  /** * 在资源服务器中 不携带token 或者token无效  401 * @author zxg */@slf4jpublic class simpleauthenticationentrypoint implements authenticationentrypoint {    @override    public void commence(httpservletrequest request, httpservletresponse response, authenticationexception authexception) throws ioexception, servletexception {        if (response.iscommitted()){            return;        }         throwable throwable = authexception.fillinstacktrace();         string errormessage = 认证失败;         if (throwable instanceof badcredentialsexception){            errormessage = 错误的客户端信息;        }else {            throwable cause = authexception.getcause();             if (cause instanceof jwtvalidationexception) {                log.warn(jwt token 过期,具体内容: + cause.getmessage());                errormessage = 无效的token信息;            } else if (cause instanceof badjwtexception){                log.warn(jwt 签名异常,具体内容: + cause.getmessage());                errormessage = 无效的token信息;            } else if (cause instanceof accountexpiredexception){                errormessage = 账户已过期;            } else if (cause instanceof lockedexception){                errormessage = 账户已被锁定;//            } else if (cause instanceof invalidclientexception || cause instanceof badclientcredentialsexception){//                response.getwriter().write(json.tojsonstring(singleresultbundle.failed(401,无效的客户端)));//            } else if (cause instanceof invalidgrantexception || cause instanceof redirectmismatchexception){//                response.getwriter().write(json.tojsonstring(singleresultbundle.failed(无效的类型)));//            } else if (cause instanceof unauthorizedclientexception) {//                response.getwriter().write(json.tojsonstring(singleresultbundle.failed(未经授权的客户端)));            } else if (throwable instanceof insufficientauthenticationexception) {                string message = throwable.getmessage();                if (message.contains(invalid token does not contain resource id)){                    errormessage = 未经授权的资源服务器;                }else if (message.contains(full authentication is required to access this resource)){                    errormessage = 缺少验证信息;                }            }else {                errormessage = 验证异常;            }        }         response.setstatus(httpservletresponse.sc_unauthorized);        response.setcharacterencoding(utf-8);        response.setcontenttype(mediatype.application_json_value);        objectmapper objectmapper = new objectmapper();        string resbody = objectmapper.writevalueasstring(singleresultbundle.failed(errormessage));        printwriter printwriter = response.getwriter();        printwriter.print(resbody);        printwriter.flush();        printwriter.close();    }} 新增 resourcecontroller 进行接口测试
@slf4j@restcontrollerpublic class resourcecontroller {     /**     * 测试spring authorization server,测试权限     */    @preauthorize(hasauthority('scope_message.read'))    @getmapping(/gettest)    public string gettest(){        return gettest;    }     /**     * 默认登录成功跳转页为 /  防止404状态     *     * @return the map     */    @getmapping(/)    public map index() {        return collections.singletonmap(msg, login success!);    }     @getmapping(/getresourcetest)    public singleresultbundle getresourcetest(){        return singleresultbundle.success(这是resource的测试方法 getresourcetest());    }} 启动项目,效果如下
项目总体结构如下
测试认证鉴权 #调用 /oauth2/authorize ,获取codehttp://127.0.0.1:9000/oauth2/authorize?client_id=zxg&response_type=code&scope=message.read&redirect_uri=http://www.baidu.com#会判断是否登录,若没有,则跳转到登录页面,如下图1#登录完成后,会提示是否授权,若没有,则跳转到授权界面,如下图2#授权成功后,跳转到回调地址,并带上code,如图3 打开postman,进行获取access_token
#访问 /oauth2/token 地址#在authorization中选择basic auth模式,填入对应客户端,其会在header中生成authorization,如下图右侧 返回结果如下
调用resourcecontroller中的接口,测试token是否生效
源码下载地址
https://gitee.com/rjj521/authorization-server-learn 总结 至此,spring-authorization-server的基础使用已完成,总体上和原spring security oauth大差不差,个别配置项不同。期间在网上搜寻了很多资料,然后进行整合,因此文中存在与其他网上教程相同代码,如有争议,请联系我删除改正,谢谢。


新唐科技N572F072简介
利用藻类和磁粒子开发微型机器人,可传递药物和远距引导
n沟道mos管和p沟道mos管详解
浅谈电子连接器对尼龙材质的要求
中芯国际可能研发出14nm工艺 台媒:三星在坏中芯国际的好事
为什么使用spring-authorization-server?
点成动态丨 2023中国(南京)国际科教技术及装备博览会(CESEE)精彩回顾
电子线对我们有着非常巨大的实际性价值
当3D打印应用在无人机有何不同?
STM32复位来源(寄存器版)
华硕灵珑ASUSPRO评测 算是出类拔萃的优秀产品了
热敏电阻和热电阻有什么区别
PCB设计中焊盘的设计标准解析
电能质量分析仪可用电压基准源GREF10XX,10ppm/°C温漂
基于FPGA的磁浮轴承控制系统研究
安士佳携手亿智电子共同推动AI芯片在万物智联领域的应用
福布斯中国发布最佳CEO排名 比亚迪王传福和宁德时代曾毓群上榜
IFA2019,将有哪些趋势性的技术亮点呈现?
设备拆卸的基本原则及常用方法
Borosil推出了一种名为“ Suraksha”的UV-C消毒设备