OAuth + Security - 6 - 自定義授權模式

我們知道OAuth2的官方提供了四種令牌的獲取椅文,簡化模式冒晰,授權碼模式蛉加,密碼模式蚜枢,客戶端模式。其中密碼模式中僅僅支持我們通過用戶名和密碼的方式獲取令牌针饥,那么我們?nèi)绾稳崿F(xiàn)一個我們自己的令牌獲取的模式呢祟偷?下面我們將以用戶名,密碼打厘,角色三個信息的方式來獲取令牌。

在授權模式中贺辰,授權模式的核心接口是 TokenGranter 户盯,他擁有一個抽象實現(xiàn)類 AbstractTokenGranter 嵌施,我們需要自定義新的 grant type ,就再寫一個他的子類即可莽鸭,如下:

public class AccountRoleTokenGranter extends AbstractTokenGranter {

    private static final String GRANT_TYPE = "password_role";
    
    // 獲取用戶信息的實現(xiàn)
    private UserRoleDetailServiceImpl userRoleDetailService;

    /**
     * 構造方法提供一些必要的注入的參數(shù)
     * 通過這些參數(shù)來完成我們父類的構建
     *
     * @param tokenServices         tokenServices
     * @param clientDetailsService  clientDetailsService
     * @param oAuth2RequestFactory  oAuth2RequestFactory
     * @param userRoleDetailService userDetailsService
     */
    public AccountRoleTokenGranter(AuthorizationServerTokenServices tokenServices,
                                   ClientDetailsService clientDetailsService,
                                   OAuth2RequestFactory oAuth2RequestFactory,
                                   UserRoleDetailServiceImpl userRoleDetailService) {
        super(tokenServices, clientDetailsService, oAuth2RequestFactory, GRANT_TYPE);
        this.userRoleDetailService = userRoleDetailService;
    }

    /**
     * 在這里查詢我們用戶吗伤,構建用戶的授權信息
     *
     * @param client       客戶端
     * @param tokenRequest tokenRequest
     * @return OAuth2Authentication
     */
    @Override
    protected OAuth2Authentication getOAuth2Authentication(ClientDetails client, TokenRequest tokenRequest) {
        Map<String, String> params = tokenRequest.getRequestParameters();
        String account = params.getOrDefault("username", "");
        String role = params.getOrDefault("role", "");

        // 獲取用戶信息
        UserDetails userDetails = userRoleDetailService.loadUserByAccountAndRole(account, role);
        if (ObjectUtil.isNull(userDetails)) {
            throw new UsernameNotFoundException("用戶角色不存在");
        }
        // 構建用戶授權信息
        Authentication user = new AccountRoleAuthenticationToken(userDetails.getUsername(),
                userDetails.getPassword(), userDetails.getAuthorities());
        return new OAuth2Authentication(tokenRequest.createOAuth2Request(client), user);
    }

}

配置用戶信息獲取實現(xiàn)類UserRoleDetailServiceImpl

@Service
public class UserRoleDetailServiceImpl {

    private UserService userService;
    private RoleService roleService;

    @Autowired
    public UserRoleDetailServiceImpl(UserService userService, RoleService roleService) {
        this.userService = userService;
        this.roleService = roleService;
    }

    public UserCredential loadUserByAccountAndRole(String account, String roles) throws UsernameNotFoundException {
        // 查詢相應用戶
        UserDetailDTO userCredential = userService.findByAccountAndRole(account, roles);

        if (ObjectUtils.isEmpty(userCredential)) {
            throw new UsernameNotFoundException("該賬號角色不存在!");
        }

        Set<GrantedAuthority> grantedAuthorities = Sets.newHashSet();
        List<Role> roleResult = userService.findRoleByUserId(Integer.valueOf(userCredential.getUserId()));
        if (!roleResult.isEmpty()) {
            for (Role role : roleResult) {
                if (StrUtil.equalsIgnoreCase(role.getRoleName(), roles)) {
                    //角色必須是ROLE_開頭,可以在數(shù)據(jù)庫中設置
                    GrantedAuthority grantedAuthority = new SimpleGrantedAuthority(role.getRoleName());
                    grantedAuthorities.add(grantedAuthority);
                    //獲取權限
                    List<MenuDTO> menuByRoleId = roleService.findMenuByRoleId(role.getRoleId());
                    if (!menuByRoleId.isEmpty()) {
                        for (MenuDTO menu : menuByRoleId) {
                            if (StringUtils.isNotBlank(menu.getPerms())) {
                                GrantedAuthority authority = new SimpleGrantedAuthority(menu.getPerms());
                                grantedAuthorities.add(authority);
                            }
                        }
                    }
                }

            }
        }
        UserCredential authUser = new UserCredential(account, userCredential.getPassword(), grantedAuthorities);
        BeanUtils.copyProperties(userCredential, authUser);
        return authUser;

    }
}

/**
 * 認證用戶信息類
 *
 * @author zhongyj <1126834403@qq.com><br/>
 * @date 2020/2/25
 */
@Setter
@Getter
public class UserCredential extends User implements Serializable {

    private static final long serialVersionUID = 2554837818190360741L;

    public static final String DEFAULT_USER = "dimples";

    private boolean accountNonExpired = true;

    private boolean accountNonLocked = true;

    private boolean credentialsNonExpired = true;

    private boolean enabled = true;

    private Integer userId;

    private String userCode;

    private String account;

    private String username;

    private String status;

    private Date createDate;

    private Date modifyDate;


    public UserCredential(String username, String password, Collection<? extends GrantedAuthority> authorities) {
        super(username, password, authorities);
        this.account = username;
    }

    public UserCredential(String username, String password, boolean enabled, boolean accountNonExpired, boolean credentialsNonExpired, boolean accountNonLocked, Collection<? extends GrantedAuthority> authorities) {
        super(username, password, enabled, accountNonExpired, credentialsNonExpired, accountNonLocked, authorities);
    }
}

接下來就只需將其添加到Oauth2AuthorizationServerConfig 中

@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
    endpoints
            // 配置token存儲源
            .tokenStore(tokenStore())
            // 配置權限管理
            .authenticationManager(authenticationManager);
    endpoints.tokenGranter(tokenGranter(endpoints));
}

/**
 * 重點
 * 先獲取已經(jīng)有的五種授權硫眨,然后添加我們自己的進去
 *
 * @param endpoints AuthorizationServerEndpointsConfigurer
 * @return TokenGranter
 */
private TokenGranter tokenGranter(final AuthorizationServerEndpointsConfigurer endpoints) {
    List<TokenGranter> granters = new ArrayList<>(Collections.singletonList(endpoints.getTokenGranter()));
    granters.add(new AccountRoleTokenGranter(
            endpoints.getTokenServices(),
            endpoints.getClientDetailsService(),
            endpoints.getOAuth2RequestFactory(),
            userRoleDetailService));
    return new CompositeTokenGranter(granters);
}
image

參考資料:

https://github.com/spring-projects/spring-security-oauth/blob/master/tests/annotation/custom-grant/src/main/java/demo/Application.java

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末足淆,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子礁阁,更是在濱河造成了極大的恐慌巧号,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,542評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件姥闭,死亡現(xiàn)場離奇詭異丹鸿,居然都是意外死亡,警方通過查閱死者的電腦和手機棚品,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評論 3 394
  • 文/潘曉璐 我一進店門靠欢,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人铜跑,你說我怎么就攤上這事门怪。” “怎么了锅纺?”我有些...
    開封第一講書人閱讀 163,912評論 0 354
  • 文/不壞的土叔 我叫張陵掷空,是天一觀的道長。 經(jīng)常有香客問我伞广,道長拣帽,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,449評論 1 293
  • 正文 為了忘掉前任嚼锄,我火速辦了婚禮减拭,結果婚禮上,老公的妹妹穿的比我還像新娘区丑。我一直安慰自己拧粪,他們只是感情好,可當我...
    茶點故事閱讀 67,500評論 6 392
  • 文/花漫 我一把揭開白布沧侥。 她就那樣靜靜地躺著可霎,像睡著了一般。 火紅的嫁衣襯著肌膚如雪宴杀。 梳的紋絲不亂的頭發(fā)上癣朗,一...
    開封第一講書人閱讀 51,370評論 1 302
  • 那天,我揣著相機與錄音旺罢,去河邊找鬼旷余。 笑死绢记,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的正卧。 我是一名探鬼主播蠢熄,決...
    沈念sama閱讀 40,193評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼炉旷!你這毒婦竟也來了签孔?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,074評論 0 276
  • 序言:老撾萬榮一對情侶失蹤窘行,失蹤者是張志新(化名)和其女友劉穎饥追,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體抽高,經(jīng)...
    沈念sama閱讀 45,505評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡判耕,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,722評論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了翘骂。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片壁熄。...
    茶點故事閱讀 39,841評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖碳竟,靈堂內(nèi)的尸體忽然破棺而出草丧,到底是詐尸還是另有隱情,我是刑警寧澤莹桅,帶...
    沈念sama閱讀 35,569評論 5 345
  • 正文 年R本政府宣布昌执,位于F島的核電站,受9級特大地震影響诈泼,放射性物質(zhì)發(fā)生泄漏懂拾。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,168評論 3 328
  • 文/蒙蒙 一铐达、第九天 我趴在偏房一處隱蔽的房頂上張望岖赋。 院中可真熱鬧,春花似錦瓮孙、人聲如沸唐断。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽脸甘。三九已至,卻和暖如春偏灿,著一層夾襖步出監(jiān)牢的瞬間丹诀,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留忿墅,地道東北人扁藕。 一個月前我還...
    沈念sama閱讀 47,962評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像疚脐,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子邢疙,可洞房花燭夜當晚...
    茶點故事閱讀 44,781評論 2 354