As you may suspect, it very much depends on the hardware. In general, you are correct, JTAG and/or UARTs can be often be used to get a copy of the firmware (downloading a firmware update from the vendor is usually the easiest way of course, but I'm assuming that is not what you mean).
JTAG implementations typically allow you to read/write memory, and flash chips are typically "mapped" into memory at some pre-defined address (finding that address is usually a matter of Googling, experience, and trial and error); thus, you can use tools like UrJTAG and OpenOCD to read the contents of flash.
UART is just a serial port, so what interface or options it provides (if any) is entirely up to the developer who created the system; most bootloaders (e.g., U-Boot) do allow you to read/write flash/memory, and will dump the ASCII hex to your terminal window. You then would need to parse the hexdump and convert it into actual binary values. Again, YMMV and there may be no way to dump memory or flash via the UART.
Other devices may have other mechanisms that provide similar functionality; for example, Microchip's PIC microcontrollers use ICSP (In Circuit Serial Programming) interfaces to read, write, and debug firmware. Such interfaces are usually proprietary, and may or may not be documented (Microchip's is well known).
Vendors may take steps to protect or disable debug interfaces such as JTAG, UART and ICSP, but often you can dump the flash chip directly (this is usually faster than JTAG/UART, but may require some de/soldering). For devices such as microcontrollers that have the flash chip built-in (i.e., the flash chip is not exposed to you), you may need to resort to more advanced techniques for defeating such copy-protections.
Personally, since I don't deal much with microcontroller based systems, dumping the flash chip directly is usually my go-to for grabbing a copy of the firmware from the device.