在用户服务中,oauth2认证的时候,客户端是在代码中指定的。只有一个,这里将它移到数据库中。并提供API可以通过接口维护客户端。
之前项目中客户端这段是这么写的:
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.inMemory()
.withClient("client")
.secret(new BCryptPasswordEncoder().encode("secret"))
.authorizedGrantTypes("client_credentials", "password", "refresh_token", "authorization_code")
.scopes("all", "user_info")
.autoApprove(false) // true: 不会跳转到授权页面
.redirectUris("http://localhost:8080/login");
}
下面开始允许多个客户端,而且客户端是可配置的。
创建数据模型
client.java
@Data
@Entity
@Table(name = "bh_user_client")
public class Client implements Serializable {
private static final long serialVersionUID = -6421664309310055644L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@Column(name = "client_name")
private String clientName; // 客户端名称
@Column(name = "client_id")
private String clientId; // 客户端ID
@Column(name = "resource_ids")
private String resourceIds;
@Column(name = "client_secret")
private String clientSecret; // 客户端密码
private String scope; // 客户端权限范围
@Column(name = "authorized_grant_types")
private String authorizedGrantTypes; // 客户端可请求的认证类型
@Column(name = "web_server_redirect_uri", length = 4096)
private String webServerRedirectUri; // 跳转地址
private String authorities; // 权限
@Column(name = "access_token_validity")
private Integer accessTokenValidity; // token有效时间
@Column(name = "refresh_token_validity")
private Integer refreshTokenValidity; // 刷新token有效时间
@Column(name = "additional_infomation")
private String additionalInformation; // 补充信息
private String autoapprove;
@Column(name = "registered_redirect_uri")
private String registeredRedirectUri;
@Column(name = "create_time")
private Long createTime; // 创建时间
private int self = 1; // 是不是自己平台的项目
}
ClientRepository.java
public interface ClientRepository extends CustomRepository {
Client findByClientNameAndIdNot(String name, Integer id);
Client findByClientIdAndIdNot(String clientId, Integer id);
}
ClientService.java
public interface ClientService {
/**
* 添加/修改信息
*
* @param client
* @return
* @throws EberException
*/
public Client save(Client client) throws BhException;
/**
* 根据id删除信息
*
* @param id
* @return
* @throws EberException
*/
public Client delete(Integer id);
/**
* 根据客户端名称加载信息
*
* @param name
* @return
*/
public Client load(Integer id, String name, String clientId);
/**
* 加载所有信息
*
* @return
*/
public List list();
/**
* 当前请求的客户端
* @return
* @throws EberException
*/
public Client current();
public Set listClientGrantedAuthorities(String clientId);
}
实现service
package com.biboheart.huip.user.service.impl;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.stereotype.Service;
import com.biboheart.brick.exception.BhException;
import com.biboheart.brick.utils.CheckUtils;
import com.biboheart.brick.utils.TimeUtils;
import com.biboheart.huip.user.domain.Client;
import com.biboheart.huip.user.repository.ClientRepository;
import com.biboheart.huip.user.service.ClientService;
@Service
public class ClientServiceImpl implements ClientService {
@Autowired
private ClientRepository clientRepository;
@Override
public Client save(Client client) throws BhException {
if(null == client.getId()) {
client.setId(0);
}
if(CheckUtils.isEmpty(client.getClientName())) {
throw new BhException("名称不能为空");
}
Client source = clientRepository.findByClientNameAndIdNot(client.getClientName(), client.getId());
if (null != source && source.getId() != client.getId()) {
throw new BhException("名称已存在");
}
if(CheckUtils.isEmpty(client.getCreateTime())) {
client.setCreateTime(TimeUtils.getCurrentTimeInMillis());
}
if(null != source) {
client.setClientId(source.getClientId());
client.setClientSecret(source.getClientSecret());
}
if(CheckUtils.isEmpty(client.getClientId()) || CheckUtils.isEmpty(client.getClientSecret())) {
client.setClientId(DigestUtils.md5Hex(client.getClientName() + "_client_" + UUID.randomUUID().toString()));
client.setClientSecret(DigestUtils.md5Hex(client.getClientName() + "_secret_" + UUID.randomUUID().toString()));
}
client.setScope("read,write,trust");
client = clientRepository.save(client);
return client;
}
@Override
public Client delete(Integer id) {
Client client = null;
if (CheckUtils.isEmpty(id)) {
return null;
}
client = clientRepository.findById(id).get();
if (null == client) {
return null;
}
clientRepository.delete(client);
return client;
}
@Override
public Client load(Integer id, String name, String clientId) {
Client client = null;
if(!CheckUtils.isEmpty(id)) {
client = clientRepository.findById(id).get();
}
if(null == client && !CheckUtils.isEmpty(name)) {
client = clientRepository.findByClientNameAndIdNot(name, 0);
}
if(null == client && !CheckUtils.isEmpty(clientId)) {
client = clientRepository.findByClientIdAndIdNot(clientId, 0);
}
return client;
}
@Override
public List list() {
List clients = clientRepository.findAll();
return clients;
}
@Override
public Client current() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if(null == authentication) {
return null;
}
String clientId = ((OAuth2Authentication) authentication).getOAuth2Request().getClientId();
if(CheckUtils.isEmpty(clientId)) {
return null;
}
Client client = clientRepository.findByClientIdAndIdNot(clientId, 0);
return client;
}
@Override
public Set listClientGrantedAuthorities(String clientId) {
Set authorities = new HashSet();
if(CheckUtils.isEmpty(clientId)) {
return authorities;
}
authorities.add(new SimpleGrantedAuthority("ROLE_CLIENT"));
return authorities;
}
}
开放API
ClientController.java
package com.biboheart.huip.user.controller;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.biboheart.brick.exception.BhException;
import com.biboheart.brick.model.BhResponseResult;
import com.biboheart.huip.user.domain.Client;
import com.biboheart.huip.user.service.ClientService;
@RestController
public class ClientController {
@Autowired
private ClientService clientService;
/**
* 保存客户端
* @param client
* @return
* @throws EberException
*/
@RequestMapping(value = "/userapi/client/save", method = {RequestMethod.POST})
public BhResponseResult> save(Client client) throws BhException {
client = clientService.save(client);
return new BhResponseResult<>(0, "success", client);
}
/**
* 更新客户端ID
* @param id
* @return
* @throws EberException
*/
@RequestMapping(value = "/userapi/client/update", method = {RequestMethod.POST, RequestMethod.GET})
public BhResponseResult> update(Integer id) throws BhException {
Client client = clientService.load(id, null, null);
if (null == client) {
throw new BhException("客户端不存在");
}
client.setClientId(null);
client.setClientSecret(null);
client = clientService.save(client);
return new BhResponseResult<>(0, "success", client);
}
/**
* 删除客户端
* @param id
* @return
*/
@RequestMapping(value = "/userapi/client/delete", method = {RequestMethod.POST, RequestMethod.GET})
public BhResponseResult> delete(Integer id) {
Client client = clientService.delete(id);
return new BhResponseResult<>(0, "success", client);
}
/**
* 查询客户端
* @param id
* @param name
* @param clientId
* @return
*/
@RequestMapping(value = "/userapi/client/load", method = {RequestMethod.POST, RequestMethod.GET})
public BhResponseResult> load(Integer id, String name, String clientId) {
Client client = clientService.load(id, name, clientId);
return new BhResponseResult<>(0, "success", client);
}
/**
* 客户端列表
* @return
*/
@RequestMapping(value = "/userapi/client/list", method = {RequestMethod.POST, RequestMethod.GET})
public BhResponseResult> list() {
List clients = clientService.list();
return new BhResponseResult<>(0, "success", clients);
}
}
在com.biboheart.huip.user.security包中创建CustomClientDetailsService实现ClientDetailsService
package com.biboheart.huip.user.security;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.oauth2.provider.ClientDetails;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.ClientRegistrationException;
import org.springframework.security.oauth2.provider.NoSuchClientException;
import org.springframework.security.oauth2.provider.client.BaseClientDetails;
import com.biboheart.brick.utils.CheckUtils;
import com.biboheart.huip.user.domain.Client;
import com.biboheart.huip.user.service.ClientService;
@Component("customClientDetailsService")
public class CustomClientDetailsService implements ClientDetailsService {
@Autowired
private ClientService clientService;
@Override
public ClientDetails loadClientByClientId(String clientId) throws ClientRegistrationException {
ClientDetails details;
Client client = clientService.load(null, null, clientId);
if(null == client) {
throw new NoSuchClientException("没有找到ID为:" + clientId + "的客户端");
}
details = clientToClientDetails(client);
return details;
}
private ClientDetails clientToClientDetails(Client client) {
if(null == client) {
return null;
}
Set authorities = clientService.listClientGrantedAuthorities(client.getClientId());
BaseClientDetails details = new BaseClientDetails(client.getClientId(), client.getResourceIds(), client.getScope(),
client.getAuthorizedGrantTypes(), client.getAuthorities(), client.getRegisteredRedirectUri());
details.setClientSecret(client.getClientSecret());
details.setAccessTokenValiditySeconds(client.getAccessTokenValidity());
details.setRefreshTokenValiditySeconds(client.getRefreshTokenValidity());
details.setAuthorities(authorities);
Set autoApproveScopes = new HashSet<>();
if (!CheckUtils.isEmpty(client.getSelf())) {
autoApproveScopes.add("true");
}
details.setAutoApproveScopes(autoApproveScopes);
details.setAdditionalInformation(new HashMap());
return details;
}
}
修改AuthorizationServerConfiguration
@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
@Autowired
@Qualifier("authenticationManagerBean")
private AuthenticationManager authenticationManager;
@Autowired
@Qualifier("customClientDetailsService")
private ClientDetailsService clientDetailsService;
@Autowired
private UserDetailsService customUserDetailsService;
@Autowired
private TokenStore tokenStore;
@Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients.withClientDetails(clientDetailsService);
}
...略...
}
这样就可以根据数据库中的客户端进行权限认证及授权。