2121import lombok .Setter ;
2222import org .hswebframework .web .authorization .*;
2323
24+ import javax .annotation .concurrent .NotThreadSafe ;
2425import java .io .Serial ;
2526import java .io .Serializable ;
2627import java .util .*;
@@ -46,7 +47,6 @@ public class SimpleAuthentication implements Authentication {
4647
4748 private List <Dimension > dimensions = new ArrayList <>();
4849
49- @ Setter
5050 private Map <String , Serializable > attributes = new HashMap <>();
5151
5252 public static Authentication of () {
@@ -72,6 +72,26 @@ public Map<String, Serializable> getAttributes() {
7272 return attributes == null ? Collections .emptyMap () : attributes ;
7373 }
7474
75+ public void setAttributes (Map <String , Serializable > attributes ) {
76+ this .attributes = new HashMap <>(attributes );
77+ }
78+
79+ @ Override
80+ public void setAttribute (String key , Serializable value ) {
81+ if (key == null ) {
82+ return ;
83+ }
84+ if (this .attributes == null ) {
85+ this .attributes = new HashMap <>();
86+ }
87+ this .attributes .put (key , value );
88+ }
89+
90+ public void putAttributes (Map <String , Serializable > attributes ) {
91+ this .attributes = new HashMap <>(this .attributes );
92+ this .attributes .putAll (attributes );
93+ }
94+
7595 public SimpleAuthentication merge (Authentication authentication ) {
7696 Map <String , Permission > mePermissionGroup = permissions
7797 .stream ()
@@ -92,12 +112,16 @@ public SimpleAuthentication merge(Authentication authentication) {
92112 }
93113 me .getActions ().addAll (permission .getActions ());
94114 }
115+ //merge 保持 self 优先:仅当 self 不含该 (type,id) 时才并入 other 的维度
95116 this .dimensions = new ArrayList <>(this .getDimensions ());
96117 for (Dimension dimension : authentication .getDimensions ()) {
97- if (getDimension ( dimension . getType () , dimension . getId ()). isEmpty ( )) {
98- dimensions .add (dimension );
118+ if (! containsDimension ( this . dimensions , dimension )) {
119+ this . dimensions .add (dimension );
99120 }
100121 }
122+ //重建了 permissions/dimensions 列表,失效索引缓存
123+ this .permissionMapping = null ;
124+ this .dimensionMapping = null ;
101125 return this ;
102126 }
103127
@@ -128,23 +152,116 @@ public Authentication copy(BiPredicate<Permission, String> permissionFilter,
128152
129153 public void setUser (User user ) {
130154 this .user = user ;
131- dimensions . add (user );
155+ addDimension (user );
132156 }
133157
134158 protected void setUser0 (User user ) {
135159 this .user = user ;
136160 }
137161
162+ protected List <Dimension > newDimensions () {
163+ this .dimensions = new ArrayList <>();
164+ if (user != null ) {
165+ this .dimensions .add (user );
166+ }
167+ return this .dimensions ;
168+ }
169+
138170 public void setDimensions (List <Dimension > dimensions ) {
139- this .dimensions .addAll (dimensions );
171+ this .dimensions = distinctLastWins (newDimensions (), dimensions );
172+ this .dimensionMapping = null ;
140173 }
141174
142175 public void setDimensions (Collection <Dimension > dimensions ) {
143- this .dimensions .addAll (dimensions );
176+ this .dimensions = distinctLastWins (newDimensions (), dimensions );
177+ this .dimensionMapping = null ;
144178 }
145179
146180 public void addDimension (Dimension dimension ) {
147- this .dimensions .add (dimension );
181+ if (dimension != null ) {
182+ List <Dimension > target = writableDimensions ();
183+ int index = indexOfDimension (target , dimension );
184+ //已存在相同 (type,id) 则替换为最新,否则追加;线性查找避免分配索引结构
185+ if (index >= 0 ) {
186+ target .set (index , dimension );
187+ } else {
188+ target .add (dimension );
189+ }
190+ }
191+ this .dimensionMapping = null ;
192+ }
193+
194+ public void cleanPermissions () {
195+ this .permissions = new ArrayList <>();
196+ this .permissionMapping = null ;
197+ }
198+
199+ protected List <Dimension > writableDimensions () {
200+ return this .dimensions == null ? this .dimensions = new ArrayList <>() : this .dimensions ;
201+ }
202+
203+ public void addDimensions (Collection <? extends Dimension > dimensions ) {
204+ this .dimensions = distinctLastWins (getDimensions (), dimensions );
205+ this .dimensionMapping = null ;
206+ }
207+
208+ /**
209+ * 以 seed 为基础并入 toAdd,按 (type,id) 去重;命中相同标识时<b>后者覆盖前者(替换为最新)</b>,
210+ * 并保持该标识首次出现的位置.
211+ */
212+ private static List <Dimension > distinctLastWins (List <Dimension > seed ,
213+ Collection <? extends Dimension > toAdd ) {
214+ //结构化 (type,id) -> 维度,LinkedHashMap 保位置、put 覆盖实现 last-wins
215+ LinkedHashMap <List <String >, Dimension > merged = new LinkedHashMap <>();
216+ for (Dimension dimension : seed ) {
217+ merged .put (dimensionKey (dimension ), dimension );
218+ }
219+ if (toAdd != null ) {
220+ for (Dimension dimension : toAdd ) {
221+ if (dimension != null ) {
222+ merged .put (dimensionKey (dimension ), dimension );
223+ }
224+ }
225+ }
226+ return new ArrayList <>(merged .values ());
227+ }
228+
229+ //结构化 (type,id) key,避免拼接字符串带来的分隔符歧义/碰撞
230+ private static List <String > dimensionKey (Dimension dimension ) {
231+ DimensionType type = dimension .getType ();
232+ return Arrays .asList (type == null ? null : type .getId (), dimension .getId ());
233+ }
234+
235+ //merge 中判断 self 是否已含相同 (type,id);零分配线性查找
236+ private static boolean containsDimension (List <Dimension > dimensions , Dimension dimension ) {
237+ return indexOfDimension (dimensions , dimension ) >= 0 ;
238+ }
239+
240+ //线性查找相同 (type,id) 的下标,无则 -1;零分配,用于单个 add 的就地替换
241+ private static int indexOfDimension (List <Dimension > dimensions , Dimension dimension ) {
242+ DimensionType type = dimension .getType ();
243+ String typeId = type == null ? null : type .getId ();
244+ String id = dimension .getId ();
245+ for (int i = 0 ; i < dimensions .size (); i ++) {
246+ Dimension exist = dimensions .get (i );
247+ DimensionType existType = exist .getType ();
248+ if (Objects .equals (id , exist .getId ())
249+ && Objects .equals (typeId , existType == null ? null : existType .getId ())) {
250+ return i ;
251+ }
252+ }
253+ return -1 ;
254+ }
255+
256+ public void addPermissions (Collection <? extends Permission > permissions ) {
257+ this .permissions = new ArrayList <>(getPermissions ());
258+ this .permissions .addAll (permissions );
259+ this .permissionMapping = null ;
260+ }
261+
262+ public void addPermission (Permission permission ) {
263+ this .permissions .add (permission );
264+ this .permissionMapping = null ;
148265 }
149266
150267 private transient volatile Map <String , Map <String , Dimension >> dimensionMapping ;
@@ -158,21 +275,21 @@ protected boolean fastPath() {
158275 permissionMapping = permissions == null
159276 ? Collections .emptyMap ()
160277 : permissions
161- .stream ()
162- .collect (Collectors
163- .toMap (Permission ::getId ,
164- Function .identity (),
165- (a , b ) -> b ));
278+ .stream ()
279+ .collect (Collectors
280+ .toMap (Permission ::getId ,
281+ Function .identity (),
282+ (a , b ) -> b ));
166283 dimensionMapping = dimensions == null
167284 ? Collections .emptyMap ()
168285 : dimensions
169- .stream ()
170- .collect (Collectors
171- .groupingBy (d -> d .getType ().getId (),
172- Collectors .toMap (
173- Dimension ::getId ,
174- Function .identity (),
175- (a , b ) -> a )));
286+ .stream ()
287+ .collect (Collectors
288+ .groupingBy (d -> d .getType ().getId (),
289+ Collectors .toMap (
290+ Dimension ::getId ,
291+ Function .identity (),
292+ (a , b ) -> a )));
176293 }
177294 }
178295 return permissionMapping != null ;
0 commit comments