在用戶服務(wù)中睬魂,oauth2認(rèn)證的時(shí)候模庐,客戶端是在代碼中指定的。只有一個(gè)酪惭,這里將它移到數(shù)據(jù)庫中希痴。并提供API可以通過接口維護(hù)客戶端。
之前項(xiàng)目中客戶端這段是這么寫的:
@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: 不會(huì)跳轉(zhuǎn)到授權(quán)頁面
.redirectUris("http://localhost:8080/login");
}
下面開始允許多個(gè)客戶端春感,而且客戶端是可配置的砌创。
創(chuàng)建數(shù)據(jù)模型
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; // 客戶端權(quán)限范圍
@Column(name = "authorized_grant_types")
private String authorizedGrantTypes; // 客戶端可請(qǐng)求的認(rèn)證類型
@Column(name = "web_server_redirect_uri", length = 4096)
private String webServerRedirectUri; // 跳轉(zhuǎn)地址
private String authorities; // 權(quán)限
@Column(name = "access_token_validity")
private Integer accessTokenValidity; // token有效時(shí)間
@Column(name = "refresh_token_validity")
private Integer refreshTokenValidity; // 刷新token有效時(shí)間
@Column(name = "additional_infomation")
private String additionalInformation; // 補(bǔ)充信息
private String autoapprove;
@Column(name = "registered_redirect_uri")
private String registeredRedirectUri;
@Column(name = "create_time")
private Long createTime; // 創(chuàng)建時(shí)間
private int self = 1; // 是不是自己平臺(tái)的項(xiàng)目
}
ClientRepository.java
public interface ClientRepository extends CustomRepository<Client, Integer> {
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;
/**
* 根據(jù)id刪除信息
*
* @param id
* @return
* @throws EberException
*/
public Client delete(Integer id);
/**
* 根據(jù)客戶端名稱加載信息
*
* @param name
* @return
*/
public Client load(Integer id, String name, String clientId);
/**
* 加載所有信息
*
* @return
*/
public List<Client> list();
/**
* 當(dāng)前請(qǐng)求的客戶端
* @return
* @throws EberException
*/
public Client current();
public Set<GrantedAuthority> listClientGrantedAuthorities(String clientId);
}
實(shí)現(xiàn)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<Client> list() {
List<Client> 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<GrantedAuthority> listClientGrantedAuthorities(String clientId) {
Set<GrantedAuthority> authorities = new HashSet<GrantedAuthority>();
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<Client> clients = clientService.list();
return new BhResponseResult<>(0, "success", clients);
}
}
在com.biboheart.huip.user.security包中創(chuàng)建CustomClientDetailsService實(shí)現(xiàn)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<GrantedAuthority> 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<String> autoApproveScopes = new HashSet<>();
if (!CheckUtils.isEmpty(client.getSelf())) {
autoApproveScopes.add("true");
}
details.setAutoApproveScopes(autoApproveScopes);
details.setAdditionalInformation(new HashMap<String, Object>());
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);
}
...略...
}
這樣就可以根據(jù)數(shù)據(jù)庫中的客戶端進(jìn)行權(quán)限認(rèn)證及授權(quán)。