OAuth + Security

我们知道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 <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));
}

/**
 * 重点
 * 先获取已经有的五种授权,然后添加我们自己的进去
 *
 * @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

原文地址:https://www.cnblogs.com/reroyalup/p/13071157.html