As a kind of recursion, you can use Stream.reduce method. First prepare a list of possible combinations of characters for each character-position, and then consecutively reduce the stream of these lists to a single list, by summing the pairs of list elements.
As a list element, you can use Map<Integer,String>, where key - the position of the character in the string, value - the character itself, and summ the contents of these maps, excluding those keys that are already present.
And you get a list of permutations of characters.
For example, if you have a four-character string , then you have to pass through three steps of reduction, consecutively accumulating the results:
| str1 + str2 = sum1 | 
sum1 + str3 = sum2 | 
sum2 + str4 = total | 
 +  =   +  =   +  =   | 
 +  =   +  =   +  =   +  =   +  =   +  =   | 
 +  =   +  =   +  =   +  =   +  =   +  =   | 
 
15 sums for each of the 4 characters are 60 sums, resulting in 4! = 24 permutations.
Try it online!
// original string
String str = "";
// array of characters of the string
int[] codePoints = str.codePoints().toArray();
// contents of the array
System.out.println(Arrays.toString(codePoints));
//[120276, 120277, 120278, 120279]
// list of permutations of characters
List<Map<Integer, String>> permutations = IntStream.range(0, codePoints.length)
        // Stream<List<Map<Integer,String>>>
        .mapToObj(i -> IntStream.range(0, codePoints.length)
                // represent each character as Map<Integer,String>
                .mapToObj(j -> Map.of(j, Character.toString(codePoints[j])))
                // collect a list of maps
                .collect(Collectors.toList()))
        // intermediate output
        //[{0=}, {1=}, {2=}, {3=}]
        //[{0=}, {1=}, {2=}, {3=}]
        //[{0=}, {1=}, {2=}, {3=}]
        //[{0=}, {1=}, {2=}, {3=}]
        .peek(System.out::println)
        // reduce a stream of lists to a single list
        .reduce((list1, list2) -> list1.stream()
                // summation of pairs of maps from two lists
                .flatMap(map1 -> list2.stream()
                        // filter out those keys that are already present
                        .filter(map2 -> map2.keySet().stream()
                                .noneMatch(map1::containsKey))
                        // join entries of two maps
                        .map(map2 -> new LinkedHashMap<Integer, String>() {{
                            putAll(map1);
                            putAll(map2);
                        }}))
                // collect into a single list
                .collect(Collectors.toList()))
        // List<Map<Integer,String>>
        .orElse(List.of(Map.of(0, str)));
// number of permutations
System.out.println(permutations.size()); // 24
// number of rows (factorial of string length - 1)
int rows = IntStream.range(1, codePoints.length)
        .reduce((a, b) -> a * b).orElse(1);
// column-wise output
IntStream.range(0, rows)
        .mapToObj(i -> IntStream.range(0, permutations.size())
                .filter(j -> j % rows == i)
                .mapToObj(permutations::get)
                .map(map -> map.toString().replace(" ", ""))
                .collect(Collectors.joining(" ")))
        .forEach(System.out::println);
//{0=,1=,2=,3=} {1=,0=,2=,3=} {2=,0=,1=,3=} {3=,0=,1=,2=}
//{0=,1=,3=,2=} {1=,0=,3=,2=} {2=,0=,3=,1=} {3=,0=,2=,1=}
//{0=,2=,1=,3=} {1=,2=,0=,3=} {2=,1=,0=,3=} {3=,1=,0=,2=}
//{0=,2=,3=,1=} {1=,2=,3=,0=} {2=,1=,3=,0=} {3=,1=,2=,0=}
//{0=,3=,1=,2=} {1=,3=,0=,2=} {2=,3=,0=,1=} {3=,2=,0=,1=}
//{0=,3=,2=,1=} {1=,3=,2=,0=} {2=,3=,1=,0=} {3=,2=,1=,0=}
See also: How do you check if a word has an anagram that is a palindrome?