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

我們知道OAuth2的官方提供了四種令牌的獲取,簡化模式,授權碼模式,密碼模式,客戶端模式。其中密碼模式中僅僅支持我們通過用戶名和密碼的方式獲取令牌,那麼我們如何去實現一個我們自己的令牌獲取的模式呢?下面我們將以用戶名,密碼,角色三個信息的方式來獲取令牌。

在授權模式中,授權模式的核心接口是 TokenGranter ,他擁有一個抽象實現類 AbstractTokenGranter ,我們需要自定義新的 grant type ,就再寫一個他的子類即可,如下:

public class AccountRoleTokenGranter extends AbstractTokenGranter {

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

    /**
     * 構造方法提供一些必要的注入的參數
     * 通過這些參數來完成我們父類的構建
     *
     * @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);
    }

}

配置用戶信息獲取實現類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_開頭,可以在數據庫中設置
                    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 <[email protected]><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));
}

/**
 * 重點
 * 先獲取已經有的五種授權,然後添加我們自己的進去
 *
 * @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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章