With the continuous changes in the company's business, Project A and the underlying DB_A database a few years ago have been transformed into core business services and core databases.
There are more and more web services that want to obtain data from the DB_A database, and the relationship between projects has gradually evolved into the following:
It is easy to see that there will be many problems according to the development trend shown above (the project relationship is a simplified version abstracted by individuals, the actual situation is much more complicated than this).
a. When an exception occurs during the running of webappA and cannot be accessed, can webappB/ webappC .... still obtain DB_A data normally?
b. Various services provided to webappB/webappC... to obtain DB_A data are concentrated in webappA. The volume of webappA will expand infinitely horizontally, which no one likes. Fat, right?
c. During the operation of the webappA project, in addition to providing its own services to users normally, it must also take into account requests from other projects to obtain data, which will inevitably cause a performance bottleneck.
Some of these problems have already appeared during the project's online advancement. It is really uncomfortable to have maintenance shutdowns every now and then and turn into a loud slap on the face of the project team.
Digression: According to the current development speed of the Internet and the business expansion of various companies, architects who can accurately predict the development direction of the project within two years/and prepare the expansion in advance are already very good. .
Someone in the project team proposed to bypass project webappA, and the remaining webappB/webappC... directly connect to DB_A for interaction, but it was quickly rejected (you have to use the database access layer of each project to be redefined and written).
Can the database access layer be separated and used as a service to allow authorized items to be accessed? As follows:
The core idea is to provide access to a specific database for an unlimited number of N wabapps. This not only avoids coupling between projects, but also improves the reuse rate of the data access layer.
I already have the idea, let’s get started, BB can’t solve the problem. It took about two days to build it and filled in countless pitfalls. Finally, it turned out to be as appropriate as I expected.
The original project cannot be open sourced due to commercial use. After I reorganized the demo, it has been open sourced to:.
For students who need to practice in this area, everything will be clear when they clone and run locally.
Requires DB_A data project to depend on dap -service-api can access the dao-service-impl service.
dao-service-api is the interface provided to the outer layer. The final presentation method is jar, and the maven project can directly depend on it.
If there are old non-maven projects, use maven-jar-plugin/maven-assembly-plugin to depend on The jars are assembled and added to the project lib.
dao-service-impl is built by cxf + spring + druid + jpa(hibernate impl) open source class library It is a pure back-end component service.
As the implementation layer of the service interface, the final presentation method is war, which can be deployed in a cluster or distributed manner. Other projects provide services.
The directory structure is clear at a glance, and it is very fast to get started with development. It has implemented simple code generation (GenCodeServlet), and the dao layer + webService layer interfaces and implementations can be automatically generated.
webSevice implements layer injection dao layer interface, and encapsulates 5 methods of adding, deleting, modifying and checking a single table. Generally speaking, there is no need to write redundant methods and avoid writing 90% of SQL.
@WebService @SOAPBinding(style = SOAPBinding.Style.RPC)public interface UserWs {/** * 通过 ID 过去单个 User 实体对象 * cxf 传输返回对象不可为null,Dao 层获取为null时 * 实例化返回空对象,判空时使用对象主键进行判断即可 * * @param id 主键ID */UserPO getUser(String id);/** * 通过类似的 PO 获取多个 User 实体对象 * * @param userPO 对照的实体对象 */List<UserPO> listUser(UserPO userPO);/** * 通过类似的 PO 获取多个 User 实体对象 * * @param userPO 对照的实体对象 * @param orderby 排序字段 * @param asc 是否升序 */List<UserPO> listUserOrdrBy(UserPO userPO, String orderby, Boolean asc);/** * 新增 User 实体对象 * * @param userPO 要新增的对象 */UserPO addUser(UserPO userPO);/** * 更新 User 实体对象 * * @param userPO 要更新的对象 */UserPO updateUser(UserPO userPO); }
The development method is simple and crude. Use tools to reversely generate hibernate database po, and access GenCodeServlet to generate dao/ws layer interface and implementation.
Add configuration file options and publish the cxf webService service. It is estimated that it will not take 5 minutes.
The published single-table service is understood as the database access layer in the caller, where it is specified in your project Inject and couple business logic.
The purpose of this module is to demonstrate how to integrate services published by cxf.
a. Spring has been integrated into the caller project (depending on dao-service-api)
<jaxws:client id="UserWs" serviceClass="com.rambo.dsd.sys.ws.inter.UserWs" address="${cxf.server.url}/UserWs"><jaxws:outInterceptors><ref bean="wss4JOutInterceptor"/></jaxws:outInterceptors></jaxws:client>
Specific usage (under the premise of spring injection)
Map<String, Object> map = new HashMap<>(); UserWs userWs = (UserWs) SpringContextUtil.getBean("UserWs"); UserPO user = userWs.getUser("031e7a36972e11e6acede16e8241c0fe"); map.put("1.获取单个用户:", user); user.setPhone("18975468245"); UserPO userPO1 = userWs.updateUser(user); map.put("2.更新单个用户:", userPO1); UserPO userPO2 = new UserPO(); userPO2.setName("rambo"); userPO2.setPasswd(SecurityUtil.encryptMD5("123456")); userPO2.setSex("男"); userPO2.setYxbz("Y"); UserPO userPO3 = userWs.addUser(userPO2); map.put("3.新增单个用户:", userPO3); UserPO userPO4 = new UserPO(); userPO4.setSex("男"); List<UserPO> userPOList = userWs.listUser(userPO4); map.put("4.获取所有的男用户:", userPOList); UserPO userPO5 = new UserPO(); userPO5.setSex("男"); List<UserPO> userPOList1 = userWs.listUserOrdrBy(userPO5, "sorts", true); map.put("5.获取所有的男用户并按照 sorts 字段排序:", userPOList1);return map;
b. Spring is not integrated in the caller project (depends on dao-service-api)
Use tools or commands to generate cxf service clients, and introduce factory mode where used Get the service instance and couple it.
UserWsImplService userWsImplService = new UserWsImplService(new URL(cxfServerUrl + "/UserWs?wsdl")); UserWs userWs = userWsImplService.getUserWsImplPort(); addWSS4JOutInterceptor(userWs); UserPO user = userWs.getUser("031e7a36972e11e6acede16e8241c0fe"); map.put("1.获取单个用户:", user); user.setPhone("18975468245"); UserPO userPO1 = userWs.updateUser(user); map.put("2.更新单个用户:", userPO1); UserPO userPO2 = new UserPO(); userPO2.setUuid(StringUtil.getUUID()); userPO2.setName("rambo"); userPO2.setPasswd(SecurityUtil.encryptMD5("123456")); userPO2.setSex("男"); userPO2.setYxbz("Y"); UserPO userPO3 = userWs.addUser(userPO2); map.put("3.新增单个用户:", userPO3); UserPO userPO4 = new UserPO(); userPO4.setSex("男"); UserPOArray userPOArray1 = userWs.listUser(userPO4); map.put("4.获取所有的男用户:", userPOArray1); UserPO userPO5 = new UserPO(); userPO5.setSex("男"); UserPOArray userPOArray2 = userWs.listUserOrdrBy(userPO5, "sorts", true); map.put("5.获取所有的男用户并按照 sorts 字段排序:", userPOArray2.getItem());
cxf uses soap communication protocol. After all, it is a service released to the outside world. , security is still very important.
Security authentication is introduced into cxf ws-security wss4j interceptor implementation, adding authentication information to the soap header.
a. Server configuration
<!--服务端安全认证回调函数--><bean id="serverAuthCallback" class="com.rambo.dsd.base.handler.CXFServerAuthHandler"/><!--安全日志认证拦截器--><bean id="wss4JInInterceptor" class="org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor"><constructor-arg><map><entry key="action" value="UsernameToken"/><entry key="passwordType" value="PasswordDigest"/><entry key="passwordCallbackRef" value-ref="serverAuthCallback"/></map></constructor-arg></bean>
Server-side implementation javax.security.auth .callback.CallbackHandler's safe callback function:
public class CXFServerAuthHandler implements CallbackHandler { protected final static Logger log = LoggerFactory.getLogger(CXFServerAuthHandler.class); private static final Map<String, String> userMap = new HashMap<String, String>(); static { userMap.put("webappA", "webappA2017"); userMap.put("webappB", "webappB2017"); } public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { for (Callback callback : callbacks) { WSPasswordCallback pc = (WSPasswordCallback) callback; String clientUsername = pc.getIdentifier(); String serverPassword = userMap.get(clientUsername); log.info(" client:{} is starting webservice...", clientUsername); int usage = pc.getUsage(); if (usage == WSPasswordCallback.USERNAME_TOKEN) { pc.setPassword(serverPassword); } else if (usage == WSPasswordCallback.SIGNATURE) { pc.setPassword(serverPassword); } } } }
b. Integrated Spring client configuration
<!--客户端安全认证回调函数--><bean id="wsClientAuthHandler" class="com.rambo.dsc.handler.WsClientAuthHandler"/><!--安全认证对外拦截器--><bean id="wss4JOutInterceptor" class="org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor"><constructor-arg><map><entry key="action" value="UsernameToken"/><entry key="user" value="webappA"/><entry key="passwordType" value="PasswordDigest"/><entry key="passwordCallbackRef" value-ref="wsClientAuthHandler"/></map></constructor-arg></bean>
Injected webService service configuration interceptor:
<jaxws:outInterceptors><ref bean="wss4JOutInterceptor"/></jaxws:outInterceptors>
Client implementation javax.security.auth.callback.CallbackHandler Security callback function:
public class WsClientAuthHandler implements CallbackHandler { public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { for (Callback callback : callbacks) { WSPasswordCallback pc = (WSPasswordCallback) callback; pc.setPassword("webappA2017"); } } }
c. Encoding for clients that do not integrate Spring
private void addWSS4JOutInterceptor(Object wsClass) { Endpoint cxfEndpoint = ClientProxy.getClient(wsClass).getEndpoint(); Map outProps = new HashMap(); outProps.put(WSHandlerConstants.ACTION, WSHandlerConstants.USERNAME_TOKEN); outProps.put(WSHandlerConstants.USER,"webappA"); outProps.put(WSHandlerConstants.MUST_UNDERSTAND, "0"); outProps.put(WSHandlerConstants.PASSWORD_TYPE, "PasswordDigest"); outProps.put(WSHandlerConstants.PW_CALLBACK_CLASS, WsClientAuthHandler.class.getName()); cxfEndpoint.getOutInterceptors().add(new WSS4JOutInterceptor(outProps)); }
The server-side security authentication in the project uses UsernameToken. cxf supports many authentication methods/password types. Of course, you can also customize the security authentication method.
4. Conclusion
The service architecture of Internet companies is blood and habits. Each company has its own routines and structures. The details are Different, but the core concepts are the same.
This practice has a bit of a microservice feel, but it is far from enough, such as service registration/routing/fault tolerance/caching... there are many, many, and the project has been open sourced to it , if you are interested in perfecting it together.
The above is the detailed content of Create an intermediate service for independent database access. For more information, please follow other related articles on the PHP Chinese website!