找回密码
 加入怎通
查看: 233|回复: 1

Spring Security实现统一登录与权限控制(springsecurity过滤器链)

[复制链接]
我来看看 发表于 2023-03-06 21:36:40 | 显示全部楼层 |阅读模式
8 `3 ]8 g8 l" @" ]" d

1 项目介绍最开始是一个单体应用,所有功能模块都写在一个项目里,后来觉得项目越来越大,于是决定把一些功能拆分出去,形成一个一个独立的微服务,于是就有个问题了,登录、退出、权限控制这些东西怎么办呢?总不能每个服务都复制一套吧,最好的方式是将认证与鉴权也单独抽离出来作为公共的服务,业务系统只专心做业务接口开发即可,完全不用理会权限这些与之不相关的东西了。

4 L) _4 q/ H4 g$ W u( V2 Q' S

于是,便有了下面的架构图:

3 U4 Y5 A4 Y9 U

下面重点看一下统一认证中心和业务网关的建设2 统一认证中心这里采用 Spring Security + Spring Security OAuth2OAuth2是一种认证授权的协议,是一种开放的标准最长用到的是授权码模式和密码模式,在本例中,用这两种模式都可以。

) d4 a# r+ k3 V3 _3 R' E! O

首先,引入相关依赖最主要的依赖是 spring-cloud-starter-oauth2 ,引入它就够了org.springframework.cloud

4 R! H$ r5 t5 A L! X$ z {

>spring-cloud-starter-oauth22.2.5.RELEASE这里Spring Boot的版本是2.6.3 完整的pom如下:

3 Z. Y. A' u) f* n9 q, L4 ~7 P

xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><

1 U: _4 y9 q, w/ F: c! x# m3 d* X

modelVersion>4.0.0com.tgftgf-service-parent

5 |( w+ z+ M, \9 s' ~

>1.3.0com.soa.supervision.uaa

$ X4 R' t; h |7 K+ q

soas-uaa0.0.1-SNAPSHOTsoas-uaa

1 ]$ E0 W. H2 T0 Y' i5 S+ n+ E6 L0 u

>1.82021.0.0

8 J# `* v' M0 I+ Y/ i

>org.springframework.bootspring-boot-starter-data-redis

, F' R9 a9 Z1 l5 b# ^

>org.springframework.bootspring-boot-starter-web

7 f9 ~# l. w9 W. ]8 `) z" P: K

org.springframework.cloudspring-cloud-starter

3 g- z0 R3 z7 m2 e% F% W

org.springframework.cloudspring-cloud-starter-oauth2

% E" l0 w2 | x1 I1 v, ~& `

2.2.5.RELEASEcom.nimbusds

( u/ |- I7 R' x& C

nimbus-jose-jwt9.19com.baomidou

) M3 M$ n4 V1 }" N! T

mybatis-plus-boot-startercom.baomidou

* r; g9 _0 I$ p# T+ b: S

mybatis-plus-generator3.5.1

9 t3 I4 z+ |2 D" u# F

>org.mybatis.scriptingmybatis-freemarker1.2.3

1 S. X9 |6 e& p% [7 ^8 H' M, P

>mysqlmysql-connector-java

5 u4 q9 \3 [" b. w& ^

>runtimeorg.projectlomboklombok

5 N7 e4 Q% i; x9 R% w* M4 c

>trueorg.springframework.boot

4 a" U P0 E8 i! D( ^$ F

>spring-boot-starter-testtest

) g& q; f0 \3 x) R

>org.springframework.cloudspring-cloud-dependencies

* _/ P1 p* T: _

${spring-cloud.version}pomimport

5 T1 `7 H5 k# e, S

>org.springframework.boot

* s4 m. V! ^2 T- N

>spring-boot-maven-pluginorg.projectlombok

2 D) ^! y+ l/ W' b+ ]

lombok

6 o. J1 O: `- d7 E7 X3 p

>配置授权服务器在授权服务器中,主要是配置如何生成Token,以及注册的客户端有哪些package com.soa.supervision.uaa.config;6 g: F: ?+ O; p1 U: O% \$ ]% x $ N& S; }$ G! L7 ?' K3 a import com.soa.supervision.uaa.constant.AuthConstants; 2 t L, d: g+ }: e

# O9 i/ e# ~: N! s- ~" |3 Z

import com.soa.supervision.uaa.domain.SecurityUser; * r" c8 O! x% S0 I# i [1 u import org.springframework.beans.factory.annotation.Autowired; & B0 g$ l' v+ e/ J

. m7 T* ~' U: k

import org.springframework.context.annotation.Bean;; u5 I, k1 H( q+ o7 H9 A2 W import org.springframework.context.annotation.Configuration; 9 J' J3 R9 S! P( o2 N% ^+ p. K$ ?5 L

I9 k1 O5 F4 V

import org.springframework.core.io.ClassPathResource;6 k* q+ C( P8 e; h7 A& J! D import org.springframework.security.authentication.AuthenticationManager;/ T. D' U F3 @

v9 H$ ~5 w1 z* E4 F! J8 @

import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;. n( c# |$ y* {: @' O9 N: L$ Z import org.springframework.security.oauth2.common.OAuth2AccessToken;) z @: Q+ V O m5 X' _3 k6 {

; l' M P6 B3 \$ h( \2 X) R

import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer; 8 u' V7 q6 t7 [0 f, q

$ _: }, l+ C5 X, q; q. f# y

import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;& C/ \* C( n3 D' X

, y& }9 J* y8 w6 E+ P! X* E

import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;8 Z1 g# ]# a# Z3 l$ b7 \( E

7 R9 V5 f, B6 ^, h

import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer; 4 J7 M8 [0 I& Z/ K% b K' ]" [

: x0 F0 j- y% C/ c! Q9 t8 Z

import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;7 ], ~( b/ Q& U* H" m( U, c

1 p1 c* S8 I& @) L; W

import org.springframework.security.oauth2.provider.OAuth2Authentication; 4 R; w" H+ j2 b& l: X import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService;5 y0 C6 e4 A9 T3 i# d) ?4 k% P0 T4 \% O: Z

. w! Q0 m5 B4 K% S/ h! G$ V

import org.springframework.security.oauth2.provider.endpoint.TokenKeyEndpoint; d. |* z+ Y) _; I import org.springframework.security.oauth2.provider.token.TokenEnhancer; ( Y7 [+ D' d a' m* b0 E

# V2 ]/ i$ v2 V, I0 l

import org.springframework.security.oauth2.provider.token.TokenEnhancerChain; ! u/ g1 X! K* ^' O import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;0 \1 ^3 c3 {, _# R0 d7 G) P. |

4 I; I- ]3 a% u0 L: h

import org.springframework.security.oauth2.provider.token.store.KeyStoreKeyFactory;, J3 x% Y7 H5 ?3 q6 P; j7 T " a* x3 P) ]2 B* v+ x, H import javax.annotation.Resource; ! r4 a( z0 M; E4 s9 x3 ?9 N5 x

1 S4 m# l" i: y

import javax.sql.DataSource; / @: _) f2 w& ]8 ? G9 k! j$ `+ d; ~ import java.security.KeyPair; 9 O/ B. R) S- W# u7 k3 C/ k$ ? import java.util.ArrayList; r3 Q/ W/ b2 t4 P% A1 o import java.util.HashMap;' N1 S) c0 T2 x$ U

- ~- `1 J) z2 I3 `

import java.util.List; X1 I" v9 L4 K8 F6 o import java.util.Map;- n. h3 i8 }% ^) v* t( N % [% Q9 k; d0 @5 H5 z+ }7 R /** 0 Q5 L4 v; a9 g4 K- t0 p5 d$ r * 授权服务器配置 ; k( t7 N- d b- E) |7 J1 g * 1、配置客户端0 S' H* I' \- d5 j1 ^* @ * 2、配置Access_Token生成 2 I e, P7 n+ A9 P) f *7 b- Q) R+ j! @ ~: j }$ k * @Author

" p5 M( B( x1 m* S$ X/ m

ChengJianSheng t: h; i1 x& M9 z% u* v- I- |5 G * @Date 2022/2/14+ P2 [# @& N6 X */@Configuration@EnableAuthorizationServerpublicclassAuthorizationServerConfig

8 k3 Y9 h" D7 c( b% Y

extendsAuthorizationServerConfigurerAdapter{& X5 D; Y+ ^4 b. g8 b * E9 J' C+ U2 C% h8 N5 K @Resourceprivate DataSource dataSource; & P) z' f) a& e0 ?+ n- q7 M @Autowired

% h; b( L2 h: Z% j* ^

private AuthenticationManager authenticationManager;, b4 n: D+ p# t 7 x( g( Q' y8 _& G. n6 C: X @Overridepublicvoidconfigure(ClientDetailsServiceConfigurer clients)

* U2 @. B$ U9 B

throws Exception {% E$ A9 n6 R+ c9 c* q9 _ clients.withClientDetails(new JdbcClientDetailsService(dataSource)); & a' ]. `% {! M } ! j, _2 s) t, y0 ^" S, }* N8 a2 G( g5 {6 x& m f1 a* ^8 {) u H* p; N

, a5 c6 a/ E2 f

@Overridepublicvoidconfigure(AuthorizationServerSecurityConfigurer security)throws Exception { ' G, K1 E! ~# U( a, g security.allowFormAuthenticationForClients(); + Q& Q5 C$ O, G w

/ T9 ?! z9 j4 Z/ S: q; `

// security.tokenKeyAccess("permitAll()");3 d( x! j6 ]! ~# I e } 1 H; ^1 T) s( D* j; ? ) Y/ V3 i2 G+ e7 J. N0 _3 B4 J" i1 P @Overridepublicvoidconfigure(AuthorizationServerEndpointsConfigurer endpoints)

8 C; d( I3 D2 @

throws Exception {8 J, K) {% n$ m( c0 I: c$ y List tokenEnhancerList = new ArrayList<>();0 l" K! U- y Z0 z tokenEnhancerList.add(jwtTokenEnhancer()); 3 s, w( }7 s8 D2 j. C6 W+ A tokenEnhancerList.add(jwtAccessTokenConverter());4 c8 f& @: O7 G( \; W5 N7 | TokenEnhancerChain tokenEnhancerChain =

, {) b4 C( a; M6 d

new TokenEnhancerChain();8 `% x T$ m: y+ P2 V% I tokenEnhancerChain.setTokenEnhancers(tokenEnhancerList); * ]2 O# D" q0 w6 k C$ Y t' N/ b5 E `6 J' M endpoints.accessTokenConverter(jwtAccessTokenConverter()) " U4 ~+ {6 M6 C5 g .tokenEnhancer(tokenEnhancerChain).authenticationManager(authenticationManager);9 {5 ]8 V! G* S* [) F$ Z }: t5 i I# Q' {+ F5 z X( n0 @ 3 s& v: Z) b- n7 f' |2 I( W

9 L- O0 M( b* Q, u

/** 7 h4 c$ ^" {; n8 y * Token增强3 a% a# J6 D$ s, e */public TokenEnhancer jwtTokenEnhancer(){ " m, @2 z0 c* r returnnew TokenEnhancer() { / R3 B/ Y- m2 x8 }3 X

0 x/ {$ O5 g3 B4 }" x" J* L2 O& B& U

@Overridepublic OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication)

`3 G3 G) R$ Z2 Y/ A

{# R% b9 w% }! N SecurityUser securityUser = (SecurityUser) authentication.getPrincipal();9 V3 j! f# R; Y: A) v Map additionalInformation =

! G C* L/ z+ _; J' A" n

new HashMap<>();% P/ {1 X' h' e. J additionalInformation.put(AuthConstants.JWT_USER_ID_KEY, securityUser.getUserId()); 8 c h9 S2 E( o; e0 R additionalInformation.put(AuthConstants.JWT_USER_NAME_KEY, securityUser.getUsername()); 8 h1 i) M6 D2 L6 Z' X additionalInformation.put(AuthConstants.JWT_DEPT_ID_KEY, securityUser.getDeptId()); # f5 ?% w; s) p2 @# P8 ~ ((DefaultOAuth2AccessToken)accessToken).setAdditionalInformation(additionalInformation);8 V' U5 I, S+ v

4 ?8 j$ S) [" {5 I* W2 {+ l3 y; ~

return accessToken;# J$ M$ B7 c1 u) i } & ?2 u" U: o) v8 V3 w+ F2 b: y }; - R& o0 ?; Z* R5 u- U } + H1 H- ~1 ^* d' K# f3 N+ B) } /**+ _0 U. H' ?6 {, c m * 采用RSA加密算法对JWT进行签名* @) `5 [; \, W6 ~ */public JwtAccessTokenConverter

/ M1 b1 I; n! j, S

jwtAccessTokenConverter(){ & f: @1 t- o8 X1 K8 W! U& O, Z JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter();2 e. e$ |5 a. V* x jwtAccessTokenConverter.setKeyPair(keyPair()); 3 @/ x& U/ Y9 \ p

2 h* z' l+ h( D* r! }. Y) g4 ?, e

return jwtAccessTokenConverter;( V, R6 S! D3 r f }: x6 j2 H3 @1 }, j( ]0 t : h) a* F% M# \$ X' { [$ u /** , x; F/ h3 L& U: k8 ]/ K * 密钥对& H* D N _$ |/ Q \- A */@Beanpublic KeyPair keyPair(){ - a+ g4 }+ F6 o KeyStoreKeyFactory keyStoreKeyFactory =

: s/ n+ c/ O) ~

new KeyStoreKeyFactory(new ClassPathResource("jwt.jks"), "123456".toCharArray()); 9 c% |$ T3 L, n: r- W6 \' p return keyStoreKeyFactory.getKeyPair(

3 _/ ~ T ~* K) Y

"jwt", "123456".toCharArray());$ q/ Q1 M0 K1 Q5 {( ]) i$ T. T } ' F' G9 o q, g1 @; D I * j7 U7 O2 ?% V6 Z+ b, x6 w @Beanpublic TokenKeyEndpoint tokenKeyEndpoint(){; h6 q8 G8 H& R4 A9 U return

7 |/ R" F7 f/ a) b. ~" f

new TokenKeyEndpoint(jwtAccessTokenConverter()); , Q# K0 f3 m$ c' c. b }* \+ _7 H X* m/ a0 q }说明:客户端是从数据库加载的密码模式下必须设置一个AuthenticationManager

' X; v% @5 z, W/ O6 S( B* B

采用JWT生成token是因为它轻量级,无需存储可以减小服务端的存储压力但是,为了实现退出功能,不得不将它存储到Redis中必须要对JWT进行加密,资源服务器在拿到客户端传的token时会去校验该token是否合法,否则客户端可能伪造token。

: g; ?6 `0 w: d# M- Z$ L

此处对token进行了增强,在token中加了几个字段分别表示用户ID和部门ID客户端表结构如下:DROPTABLEIFEXISTS`oauth_client_details`; ' I; f# v) n* t* u CREATETABLE

% q- S7 x/ g2 O+ x* u

`oauth_client_details` ( / N+ V; d! x8 }- S9 m( S `client_id`varchar(256) CHARACTERSET utf8 COLLATE utf8_general_ci NOTNULL

9 \3 ]# e' A) _7 s$ J& J% F% e0 y

COMMENT客户端ID, 5 s6 e, @/ \& L$ g, m `resource_ids`varchar(256) CHARACTERSET utf8 COLLATE utf8_general_ci NULLDEFAULTNULL, - }' `1 c% x7 j& T

: ~* F5 f9 T- I, D' n1 Y; n

`client_secret`varchar(256) CHARACTERSET utf8 COLLATE utf8_general_ci NULLDEFAULTNULLCOMMENT客户端密钥, & l( ^) T0 p. N% j, _

7 d& q% }+ o0 ~6 H5 K. L2 S0 J7 G! D

`scope`varchar(256) CHARACTERSET utf8 COLLATE utf8_general_ci NULLDEFAULTNULL,1 w+ K m6 \+ B( V8 u `authorized_grant_types`

|! f# C; M; @$ V' T

varchar(256) CHARACTERSET utf8 COLLATE utf8_general_ci NULLDEFAULTNULLCOMMENT授权类型,# ^6 Q5 ^; p5 D/ Z `web_server_redirect_uri`

. O' q ^) O g- _; Q5 D; t. N9 D

varchar(256) CHARACTERSET utf8 COLLATE utf8_general_ci NULLDEFAULTNULL,# O4 z7 D3 @4 O9 E4 k( ^/ A `authorities`varchar(256) CHARACTER

+ S: Q& Y) D. n/ `

SET utf8 COLLATE utf8_general_ci NULLDEFAULTNULL, ! a/ U( F0 M) v `access_token_validity`int(11) NULLDEFAULTNULLCOMMENT

# ^8 f$ z0 d2 u7 b

access_token的有效时间,, P% Z7 R9 M0 ?* u `refresh_token_validity`int(11) NULLDEFAULTNULLCOMMENTrefresh_token的有效时间,8 ~7 I- k& n: J' l `additional_information`

# T5 p Z6 j& v) C

varchar(4096) CHARACTERSET utf8 COLLATE utf8_general_ci NULLDEFAULTNULL, ) I0 u/ |' B; Q+ j) X# P; T: L: i `autoapprove`varchar(256)

( Q) |2 W m: H

CHARACTERSET utf8 COLLATE utf8_general_ci NULLDEFAULTNULLCOMMENT是否允许自动授权,) E3 Y9 m o. O1 B( j6 R& X2 U PRIMARY KEY (`client_id`)

; M7 J2 e2 c7 G( K1 T2 f- L5 N; D& b

USING BTREE 8 a; c2 H. B, {7 T/ r ) ENGINE = InnoDBCHARACTERSET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = DYNAMIC; , q% z3 q+ f$ R: e- i/ f; q1 v! R$ W6 t* M INSERT

, m( }5 C) m8 [: \3 M2 X" o3 Q

INTO`oauth_client_details`VALUES (hello, order-resource, $2a$10$1Vun/h63tI4C48BqLsy2Zel5q5M2VW6w8KThoMfxww49wf9uv/dKy

) T' z1 ]. o+ Z5 S

, all, authorization_code,password,refresh_token, http://www.baidu.com, NULL, 7200, 7260, NULL, true); 0 r" a& r' G: ^6 m

& D7 l8 T5 [" q) r

INSERTINTO`oauth_client_details`VALUES (sso-client-1, NULL, $2a$10$CxEwmODmsp/HOB7LloeBJeqUjotmNzjpk2WmjxtPxAeOYifQWLfhW

" J+ D! \6 c+ T. v$ |9 @

, all, authorization_code, http://localhost:9001/sso-client-1/login/oauth2/code/custom, NULL, 180, 240

4 a" c+ z" k( a+ ^- _' f; [& ^

, NULL, true);本例中采用RSA非对称加密,密钥文件用的是java自带的keytools生成的

& b6 e. B; ~. i

将来,认证服务器用私钥对token加密,然后将公钥公开package com.soa.supervision.uaa.controller;+ p# N' X# I7 u5 E& F/ |9 h " w. O' f* r" ~) I# D7 ` import com.nimbusds.jose.jwk.JWKSet;) ^6 Z+ s$ f; I9 O- X

& s! p/ \5 `& c4 j8 K3 P& y0 B

import com.nimbusds.jose.jwk.RSAKey;/ d0 {4 c+ Z$ a& m/ a7 G, T: e import org.springframework.beans.factory.annotation.Autowired; - y, |- y; o1 p; J8 @1 n import

# y9 ~5 R* `5 A! y& d

org.springframework.web.bind.annotation.GetMapping;2 J& J2 m) \8 i9 b% } import org.springframework.web.bind.annotation.RestController;1 d2 i% A9 t, u1 Y! E' r ! n- W' g* d& Y Q3 A5 ]

; A0 ~( `" e6 t+ h6 j

import java.security.KeyPair;5 l6 K0 K4 Y o* k1 T; E- l! J import java.security.interfaces.RSAPublicKey;8 K4 a- l9 f8 H4 e6 l. A import java.util.Map;/ L5 p0 e! H1 e/ g* w% p $ `& D& B9 K" s4 X* R2 j /*** }# H$ w$ c. F- n. } *

* G& x2 p! ]* v% S% V9 U

@Author ChengJianSheng ! _, _. n- S$ [- F * @Date 2022/2/15 - d. A" q h: X# w3 {" U- }1 @ */@RestControllerpublicclassKeyPairController{ $ M: B) A. ~5 d( h: F% Y1 B 3 N, T3 Y5 W% F7 O- R' O" ` @Autowired

K& v( c5 G+ {- h- @0 R

private KeyPair keyPair;9 M" T: c+ h7 r! m0 ~ ( a0 H, i& T. H0 S( }$ V1 y+ S @GetMapping("/rsa/publicKey")public Map getKey() {4 d5 Z9 A4 r) F! E RSAPublicKey publicKey = (RSAPublicKey)

2 i( [9 n: L, ~8 d3 h

this.keyPair.getPublic(); ' ^, M, k* P) |* w" |4 h9 s* F RSAKey key = new RSAKey.Builder(publicKey).build();1 K, B$ y) ]) F2 ~& X; T( w& I return new JWKSet(key).toJSONObject();) l5 `" d% k5 G( s } ) L5 l/ t& D+ b; B8 ^# I }

1 T6 w2 g( F4 T* U; u

配置WebSecurity在WebSecurity中主要是配置用户,以及哪些请求需要认证以后才能访问package com.soa.supervision.uaa.config;0 d1 ~ C( a- `5 @$ g2 Q . R5 d5 y( p1 z* b import com.soa.supervision.uaa.service.impl.UserDetailsServiceImpl; / r0 G0 N$ c" I$ N0 Y- h% z

4 t; N3 s- t( h

import org.springframework.beans.factory.annotation.Autowired;# E% D! q' ^. u9 B2 ^) d: ?6 P import org.springframework.boot.actuate.autoconfigure.security.servlet.EndpointRequest;3 L+ S, S2 }. ~2 Y

9 d2 R# H; M' Q& @, ]; c) h

import org.springframework.context.annotation.Bean;6 a* d% y: j7 y, X2 F; B; w) o1 A import org.springframework.context.annotation.Configuration;0 A- t7 R5 \. a, |7 y5 Q* k5 J w

8 }, \5 {- g: ?+ V: P2 R

import org.springframework.security.authentication.AuthenticationManager;: z1 o8 i3 C6 |* x2 g' C( D import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;( F0 K9 {* W0 ^3 C; a

; l o. C, Y/ K' K+ y

import org.springframework.security.config.annotation.web.builders.HttpSecurity;) p" i( d* o* F import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;' N E0 K" ]0 W

6 {2 W6 \1 H3 P( z

import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; d% O5 Y9 L- p! f1 f6 \

, ^1 c+ T; ^# p0 M: d. Y3 q

import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;% }8 w! e; f% o1 O0 g$ l0 [1 A# T import org.springframework.security.crypto.password.PasswordEncoder;; ^8 v" p8 s4 S/ y . i& l( i; E# n5 z

0 q' m+ N- d1 v/ u; w& G

/** 9 P; |/ ?/ w7 {8 L! p * @Author ChengJianSheng' V: u6 n9 i/ ]" w! } * @Date 2022/2/140 V; G! h3 `' { */@Configuration@EnableWebSecuritypublicclassWebSecurityConfig

- O4 k8 g: @) J" ?: D, E0 ?

extendsWebSecurityConfigurerAdapter{ # J& [* `1 c: J6 i$ H. J4 \0 W" N1 T3 W$ @. s/ }% f2 m) u @Autowiredprivate UserDetailsServiceImpl userDetailsService;0 K& |9 E4 l1 \) I ' Z. t" [5 M# L( W ^7 D9 s

) `; e' s; Q) r' S/ y: P

@Overrideprotectedvoidconfigure(HttpSecurity http)throws Exception { : ~8 h3 o A$ ]/ a http.authorizeRequests() 7 H' [2 J, D! B* ?+ D- p4 g .requestMatchers(EndpointRequest.toAnyEndpoint()).permitAll() 2 w4 L9 e! H" w1 Z8 B @, V% b; k .antMatchers(

6 c8 u3 m" R* _ F8 W7 J! _

"/rsa/publicKey", "/menu/tree").permitAll() * g9 E3 c2 X$ } .anyRequest().authenticated()- q/ \0 ~( x0 V8 a# r2 w .and().formLogin().permitAll() 1 W* e; a) Y/ e% k m0 O1 [ .and()( V6 v+ H4 f3 t V; a .csrf().disable(); + [/ z4 q6 t4 E2 Z% h2 y }2 o T+ F$ x. u1 t * \8 P1 j7 i- D- ^

2 f: s) @. k+ [# j) @

@Overrideprotectedvoidconfigure(AuthenticationManagerBuilder auth)throws Exception {* g5 U6 {3 H8 h4 e1 e8 J auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder()); ! r* Q- A; C( l" h) \) `6 v( y( h: g } ' A b2 T- h, ` # }( S( G: I9 V3 l* X9 s, d- Y

2 R6 m9 J4 O; `- ^& U Z

@Bean@Overridepublic AuthenticationManager authenticationManagerBean()throws Exception {; q4 _. d1 S% u: P& {/ J return

5 i: h6 D% z# u4 K# } G. k" x r

super.authenticationManagerBean();4 ]" N0 O* T$ m+ h }3 N3 } H* W+ s( [/ W m' C - z! ^+ P: d7 B: x0 X0 L7 u6 ^" W @Beanpublic PasswordEncoder passwordEncoder(){, E+ a5 w7 E/ R4 A& h6 ~" |

* L- }& F! M9 n# E$ j2 R, ?1 ]

returnnew BCryptPasswordEncoder();6 V4 [+ l) q; X2 X }+ P4 O/ K/ R6 Z5 ?1 v }UserDetailsService实现类package com.soa.supervision.uaa.service.impl; ( X: h, c1 G9 s. D ; X) y- D; b6 t$ z7 h3 D

9 e- r% k* f: U2 k( K; [ m. d( H S/ K2 [

import com.soa.supervision.uaa.domain.AuthUserDTO; 4 ^+ r# H" x$ W* k& } import com.soa.supervision.uaa.domain.SecurityUser; 8 I: r( W" a8 Q1 X

: h) ?, H0 D/ w: ?

import com.soa.supervision.uaa.service.SysUserService;- {8 _" U. J0 m* J2 @ import org.springframework.beans.factory.annotation.Autowired; : _% G1 c w, x R

) d# o2 h# q1 s! v: X/ e

import org.springframework.security.authentication.LockedException; ; L. O& C n7 q& h9 ^9 g import org.springframework.security.core.authority.SimpleGrantedAuthority; ! n7 U, W/ v9 o) E- a" L: i7 e6 h1 |

0 O. C" y3 H. p# N( e2 g

import org.springframework.security.core.userdetails.UserDetails;: f3 C% O* r0 `# s# `& m5 h% l+ c import org.springframework.security.core.userdetails.UserDetailsService; 5 ?) l! J& E: n( ~

5 \, G! d1 F# L; Q3 p

import org.springframework.security.core.userdetails.UsernameNotFoundException;6 ]/ M' \, P$ G" \' w' _% ^4 c import org.springframework.stereotype.Service;* N6 y4 B" r& g- t+ b- a. {# L7 J9 P& L / l4 b/ z; C3 z' r9 Q* b+ t9 }

$ p3 D! o4 d7 I1 }% B

import java.util.Set; " w. L/ W8 f0 M% Z import java.util.stream.Collectors;3 r% |8 K+ S: u ) d3 U# w" u6 _% p) ? /**, r' ]! N) ^! {9 R5 [: d1 e8 z * @Author ChengJianSheng9 Q( A* |( H' }" Y- \ * @Date 2022/2/148 g$ D9 t ~7 s2 t */

" f6 _6 \. f3 `5 z

@ServicepublicclassUserDetailsServiceImplimplementsUserDetailsService{1 `, U# M/ d5 L5 @& V- s - O" G, Z1 B- Q9 |! e @Autowiredprivate SysUserService sysUserService; 8 [8 r# u, T% ?' }2 {# U1 V; g% @" u4 e1 C5 Y

/ \' j: `4 r. M3 G. E

@Overridepublic UserDetails loadUserByUsername(String username)throws UsernameNotFoundException { ) o& [8 j+ m; n! a AuthUserDTO authUserDTO = sysUserService.getAuthUserByUsername(username); 2 K2 C3 w( ~8 G

; }, K8 C V; g) ?* K9 ~/ K A

if (null == authUserDTO) {% O& `* [: |: ^! l H3 R8 f thrownew UsernameNotFoundException("用户不存在"); 5 d" c" f+ W8 h) t6 D6 Y } 9 \) ?. E1 j9 ~

b- S/ Y# b- U- ^8 K6 s' j, ^

if (!authUserDTO.isEnabled()) { & y, I. Q: o, b# k7 ^$ w* N. V& K thrownew LockedException("账号被禁用");6 Z4 Z9 P! j: ^ } 8 H4 @9 e; V2 [ Set authorities = authUserDTO.getRoles().stream().map(SimpleGrantedAuthority::

0 ^+ Q8 Z* N, e q% J- x

new).collect(Collectors.toSet());. S1 K. p! M7 a returnnew SecurityUser(authUserDTO.getUserId(), authUserDTO.getDeptId(), authUserDTO.getUsername(), authUserDTO.getPassword(), authUserDTO.isEnabled(), authorities); 0 d: T6 ]( X4 K }" ?& |8 V! I8 F1 \1 l }

3 ?8 u- {' L2 y6 }3 {+ y

SysUserServicepackage com.soa.supervision.uaa.service; 1 x; v* q/ l( r( @/ f% u/ Z: s+ a8 R/ h import com.soa.supervision.uaa.domain.AuthUserDTO;/ A0 u$ O+ d# V1 @) \8 N

1 o& c4 ?/ A/ H6 y R

import com.soa.supervision.uaa.entity.SysUser;) F. z z- u! h import com.baomidou.mybatisplus.extension.service.IService;0 B3 }; J6 P4 u- C' G5 j 8 i. L2 r% c) k5 `

) Y3 t. u9 O: ^+ x' @$ n, i

/**& `1 d7 s' \! o* o% U& D2 @ *

! p, A2 B" z2 m( K" I# v A

' C0 e% m6 Y2 u6 `4 v * 用户表 服务类) ]" f) ]" [: p *

) L" h4 P5 s- _! H * / I$ X6 T/ Y& u, P+ |) y * @author ChengJianSheng9 \# H& ?$ s/ p) a6 Z$ Y% z * @since 2022-02-14. o0 h8 F1 o4 ^; R( ^7 p7 O */publicinterfaceSysUserService

extendsIService { ) ~0 |7 S4 _+ _: v7 s7 @ AuthUserDTO getAuthUserByUsername(String username); : K0 u* i7 P' }: M+ c/ k }AuthUserDTOpackage

4 }6 D: M8 T" r" I

com.soa.supervision.uaa.domain;4 O! i9 @1 B" F2 x4 o9 I( J 0 p' e" ?. ]3 J" W2 }0 B import lombok.Data; : I1 b& w. z) i8 X* i2 b+ N* |, M) q* z, Q import java.io.Serializable;7 ^7 ?/ \6 a! b3 B2 G. C import java.util.List;( t9 [6 ~( c: v r* x. w* j- M0 w - m0 O! n1 d* r7 q+ @

$ ]! d. Y0 z. R7 m

/** 8 E% k0 }+ P, H' }# w * @Author ChengJianSheng5 G1 Q' E }9 b7 O * @Date 2022/2/158 t8 G) ~( S# b7 D& Y( K */@DatapublicclassAuthUserDTOimplementsSerializable

' n Q, |( C% m9 F: [0 [8 x1 |

{# M7 S& J# l1 j6 U private Integer userId;8 q3 K8 I8 c) \: [ M+ i private String username; , ^9 X. t6 l# Z4 V! ` private String password;9 U' M$ [9 k$ F- b5 M private Integer deptId;- I' s2 P; t& r: k0 H+ F2 t

+ x# O& b' y' [' w4 K

privateboolean enabled;; c0 Q* R, ]7 ^7 A. t8 p9 o private List roles;1 S3 d& E- I( O8 f6 l+ m }SysUserServiceImplpackagecom.soa.supervision

: y/ _$ e1 |# b, n$ z. w

.uaa.service.impl;" q; Z+ f Q8 ^( ~1 r) o / E* J$ `, M- {! z6 u- @+ T importcom.soa.supervision.uaa.domain.AuthUserDTO;4 n9 a1 H: M3 H; N importcom.soa.supervision.uaa.entity

6 G4 b- x6 F2 P

.SysUser; 7 k. C" F( j( H" w+ o d3 ?7 Q importcom.soa.supervision.uaa.mapper.SysUserMapper;# }; Z1 p* X% y; ~ importcom.soa.supervision.uaa.service.SysUserService

. D4 |3 @1 h6 ?/ j% V4 V4 o1 I, h" S

; 9 P; H3 C+ h: i/ E- O1 U3 _ importcom.baomidou.mybatisplus.extension.service.impl.ServiceImpl;; I8 w- \$ u- [0 h' z importorg.springframework.beans.factory

9 ^- w/ m ?: \( ?+ z

.annotation.Autowired; L5 U9 u1 I7 O- X: k8 T! S importorg.springframework.stereotype.Service; % j! e* Y- i7 I2 p7 Z1 h |7 J: \6 k/ n3 a& G7 P, a /**0 f' y/ d& K0 s- ], ^9 R *

5 a2 i+ L9 p1 r+ ^

( d+ U6 O* T- v/ v * 用户表 服务实现类 B9 Z0 [8 g1 O1 E- H: z *

* y8 c6 z, ^' q: b d * - \' p! ?: k2 u& F * @author ChengJianSheng! V; E" w) @# k2 A* d0 B" J4 q2 F# C * @since 2022-02-14 I* H! b, i, r& L8 R2 e */

@Service6 Q1 m6 q/ }/ x: h$ u2 S6 ^ public class SysUserServiceImpl extends ServiceImpl implements SysUserService {, R# k4 Q/ ~1 o& Y 5 M0 N7 r, {8 V% t- N) R' A0 Z" w

# ]; |+ D- s4 X2 M

@Autowired - ^, T6 C* y; t5 v3 V private SysUserMapper sysUserMapper; ; u' k4 c4 ~: A7 }% N1 ^+ v / g8 L$ W4 ]) b4 n* j; T1 [ @Override - }2 k" z# W) o6 l1 @; b; M public AuthUserDTO getAuthUserByUsername(String username) { / I! j! _1 Q! ?/ A# J# A5 Q6 A

( e- o. q0 [4 a; `4 I4 J% T

returnsysUserMapper.selectAuthUserByUsername(username); ) }0 ? T% A9 o- l: h" I; h' `$ S }, T0 @9 T A. I. O9 L, [ }SysUserMapperpackage com.soa.supervision.uaa.mapper; % }; i4 G5 l: a5 `9 k 1 c5 y' R; Y3 C" p

( f0 a! o) g* S+ N* L7 A

import com.soa.supervision.uaa.domain.AuthUserDTO; * T9 _6 i% C5 p. j+ G import com.soa.supervision.uaa.entity.SysUser; 7 @' w$ P" V1 t* ~- Q import

( S* |& ~5 U* l4 c$ k6 i2 d. n

com.baomidou.mybatisplus.core.mapper.BaseMapper; ' Q# g1 g8 X8 M) B; d* i1 g& e( G. S6 S /** / v. _+ ~2 g& o% U, X: N, y * 用户表 Mapper 接口# ^5 w7 t0 ^; u- p *) w6 q1 s; D. M * @author ChengJianSheng $ ]$ w7 Z1 J- f( y" c, `/ y *

& }- a) w" Q3 w8 t" Q6 I8 N

@since 2022-02-14# T8 f+ S+ o* b( B3 K, o */publicinterfaceSysUserMapperextendsBaseMapper { 1 R5 n( {3 f& n5 \ AuthUserDTO selectAuthUserByUsername

# Z* @' h/ B4 ^( _; e

(String username); 7 f- I) H' Q2 M+ |; q, I }SysUserMapper.xml

3 n- j& s# f7 C% ?, x' D

"http://mybatis.org/dtd/mybatis-3-mapper.dtd">

, x6 Y2 [6 _2 P2 _* I7 ?( T' x

>

G* ~, U% h9 {7 S; ?1 C

column="id"/>

- |) U, o1 f- k( | E5 z; b

/>

; T' J* i) }* |' x

="roles"ofType="string"javaType="list">

: z: g2 R# s+ ~( E1 \ {

* d- i- F7 x9 w! c/ B SELECT & Y% r* d" d. i t1.id,# h0 R4 b9 y/ ? t1.username, * K" c2 Y8 k9 X' k0 i% F t1.`password`,7 r7 V" _2 n. h$ @0 X t1.dept_id,* V/ e6 L( v+ l E6 H t1.enabled, % G9 i: ^ j% g t3.`code` AS role_code 4 Z& P$ H4 o' K7 B& ~ FROM + X4 h9 ]# V" `4 {" l sys_user t12 [, ^& f2 ]8 n5 E9 H LEFT JOIN sys_user_role t2 ON t1.id = t2.user_id 1 ], ~; f I. s% A1 Y: K LEFT JOIN sys_role t3 ON t2.role_id = t3.id/ U1 |% W t T) \1 e WHERE q4 x, a: ]: }! F; o t1.username = #{username}: r8 b% ~) | f/ ]

6 v. K i' |* [0 o" g' y

UserDetailspackage com.soa.supervision.uaa.domain; 6 f( I3 q7 c0 O3 t" T7 Y , t0 c5 x2 B; V, S6 U$ j import lombok.AllArgsConstructor; 7 V; l6 f: w+ V% n$ G

) f7 x' \+ D+ A" u" I6 l

import org.springframework.security.core.GrantedAuthority;) y3 D3 E4 y& c1 k" K7 L' {- h0 ?' f import org.springframework.security.core.authority.SimpleGrantedAuthority;0 G0 k7 a6 Y3 D- f

0 T! l$ Y1 Z6 m* T# j' Y

import org.springframework.security.core.userdetails.UserDetails; : {- c2 c) O6 d3 W+ ~! I4 {7 T3 f! T7 P6 R) ?) R7 G import java.util.Collection;- e2 V V [; N2 G: V2 j7 s import

$ \/ u8 @1 R& x5 q3 q2 u* D

java.util.Set; . K' f w9 n( P) A/ y : ^) y8 h# ]- g$ ]# O /** $ m5 L5 }/ t/ I! t$ N; b$ t: } * @Author ChengJianSheng 8 Z" H1 k1 t) c6 C/ v1 {/ a/ N * @Date 2022/2/14 ) l7 y" w- T8 i */@AllArgsConstructorpublicclassSecurityUser

7 h0 s0 d8 ^& a; |

implementsUserDetails{6 ], p2 ^% i' C* Q7 Q /** , C: ]+ F- J4 {2 z+ ?* P. s: c * 扩展字段2 q; h. ]. ^/ q- P2 I */private Integer userId; , k% X9 R, i! z: J private Integer deptId; @# q8 I; u3 l0 K' _4 I Y4 ^ U" s3 ~# I' {+ f

6 e- w5 w& d( ]0 u% `

private String username; % i* h% I8 q1 S# ^ private String password;5 y; f5 J0 c, `' }0 i6 b2 e; k2 l privateboolean enabled;. n# D R9 q# i, ~: T. P private Set authorities;2 U. w2 N& S; {0 z * n2 t" X4 ?7 b: R5 Z# p

* L6 V: M$ H; G5 k' z" H4 w, U

@Overridepublic Collection getAuthorities() { 3 e2 K% D4 E2 Y0 {0 L0 U return authorities; 9 c, m s/ G8 M3 ~; h } 5 e0 D7 z. z& Z) ?% C% u2 F/ Z" `* |3 P; B+ |/ c3 A9 A/ T

. M. M- d! G0 e% s% d$ |. B

@Overridepublic String getPassword(){, v8 x8 m2 E- i: _. E+ ~. Y return password; 1 y& i, h R# Q7 H9 ^8 b2 e }; f9 ^+ g2 ~' g, v% I. y4 Q ) Y) ^* c9 h, w' o( Y @Overridepublic String getUsername

5 k: B9 p3 k% M2 y, b+ Z$ j

(){" ^( i4 \6 L- j& e4 o return username;- n( N/ t4 e) l% W, D3 Y } 3 E8 d, R1 c7 V# E" F4 [& D5 ?0 A' Z- B9 I0 c: S+ L! M3 N: {1 o @OverridepublicbooleanisAccountNonExpired(){ " H' T: |6 F) y0 L+ G0 l returntrue

6 E! D( p7 a$ |

; - ]" P3 w: o2 C: j1 }* N1 ~% p } F- [) S+ K0 [- ? 5 G1 d6 U( a" g0 y* I @OverridepublicbooleanisAccountNonLocked(){6 W h) E: ^0 E% C8 Z returntrue; ; n" N/ J/ ] e. I; c/ o }- ? W- Y; g& F % p: W" k9 E- U6 W" W; X0 Z1 W+ a" q j @Overridepublic

. J0 W3 l* A& [ y

booleanisCredentialsNonExpired(){ " G2 ~5 Y# f+ m1 y returntrue; ' e: Z. O: V' }/ z6 r+ \: y } ' N& B2 y" b' |5 @0 o - Y$ q/ P* k; a7 x i @OverridepublicbooleanisEnabled(){ 9 C# |4 p* V) }* |% K

. n/ D$ ?* \ O, t

return enabled;3 e* A1 i4 l/ O% j: y7 E: Y- |2 a } * z8 \- J3 c& l+ @: {3 o2 m3 v . e) D/ R8 S6 \ E7 O: Q8 l6 o public Integer getUserId(){/ c A7 O7 i6 d9 i7 n5 U3 _ return userId;: t2 [6 b6 Q* w. b% ^& q, z; v% @ } 3 C( v8 w( }& J/ N( i3 Q" w4 h, S4 q1 i public Integer

- |( [' n5 J. L" C, W2 B

getDeptId(){ - N$ B- ~6 S1 V) } return deptId; 4 [$ F3 |( x4 i N1 W) u }! w! O0 v( S+ x' w- r' m }登录默认的登录url是/login,本例中没有自定义登录页面,而是使用默认的登录页面 正常的密码模式下,输入用户名和密码,登录成功以后返回token。

2 h M0 D3 L9 g4 C. P0 y

本例中使用密码模式,所以写了个登录接口,而且也是取巧,覆盖了默认的/oauth/token端点package com.soa.supervision.uaa.controller;8 n8 m1 y3 R1 t/ ~( {# {1 X , Q, B2 p8 o- v& R8 S import com.tgf.common.domain.RespResult; 3 u& F4 T! T* V( _

2 Y8 Y8 X3 v. Y" O; \) l o0 ]/ P

import com.tgf.common.util.RespUtils; " Y- [# s- P0 p5 X! D" { import org.springframework.beans.factory.annotation.Autowired; 9 X' ~$ O( V+ p% d/ r: [5 r

, v4 d; R4 q0 m+ W" o

import org.springframework.security.oauth2.common.OAuth2AccessToken;0 b& H4 B$ c, C! {5 { import org.springframework.security.oauth2.provider.endpoint.TokenEndpoint; $ ]$ G8 `4 s7 g y0 B* w

- D; Z, ?+ L% _+ Z' N

import org.springframework.web.HttpRequestMethodNotSupportedException;5 z* u8 x: P6 z l2 B import org.springframework.web.bind.

7 O, s* ]) {/ Y, M* O

annotation.*;; P& W! M8 P3 a6 j: Q/ N& L8 B ) Z6 n$ V+ R w) \2 Y" B9 ?9 I( M) _ import java.security.Principal;. {- E# ?1 z% u! L7 C$ \1 t2 j import java.util.HashMap; 8 G1 Y) M4 Y! w* K* u: P$ z$ S) D8 s import java.util.Map; 8 i1 I7 f: {0 P* v M1 U$ A) E4 ~+ ~8 i# q7 t /** % G5 |; G/ ~# G$ h0 ? *

2 n3 K* `" ~9 l

@Author ChengJianSheng . ~% g( Y6 a9 }, b * @Date 2022/2/18 @; H# V. E0 G% ^$ R" T */@RestController@RequestMapping("/oauth")publicclassAuthorizationController

# T8 f% l0 z$ F

{" d) E$ E3 K) v {4 _ 4 h1 P( b# |+ l2 p, ^/ T5 j @Autowiredprivate TokenEndpoint tokenEndpoint; 2 S7 l/ S' z m' C7 j- I # [: [! M" s; Y5 P# ~ /**# k! K9 }3 s. [) B * 密码模式 登录 V/ D( ?4 v6 k k * @param principal & [& h+ P1 ~$ s. T4 D E( c; Y. t) s *

3 Z4 t; G% M" @; A: c* i

@param parameters $ l6 d1 n: ]: w/ o/ q * @return( O( R3 r. q7 B * @throws HttpRequestMethodNotSupportedException & C& ] S9 u/ G */@PostMapping(

4 @+ e& I; U: k% F# p* H

"/token")public RespResult postAccessToken(Principal principal, @RequestParam Map parameters) throws HttpRequestMethodNotSupportedException {: i# y! Q0 G8 h! g6 B' R- o' A; {8 W2 K OAuth2AccessToken oAuth2AccessToken = tokenEndpoint.postAccessToken(principal, parameters).getBody(); ' s8 k, X Y$ h# x* a: a! y Map map = new HashMap<>(); $ V, a+ |: ^) k* q! P% A4 K1 V

2 o+ r# ?& L: y& T

// 缓存return RespUtils.success();* E: x X' L T# v+ S$ N3 i- P } X6 O1 F) H1 ~+ Y4 {: o + p; h: U' O9 ~/ ?) p: z" l, ^ /**. W/ ?/ [9 q' \8 Q/ X * 退出7 C( ~* h) ^2 v0 O! L( T5 I * @return: o* E' t. e" u" {5 @ */@PostMapping("/logout"

$ l4 j+ [/ K5 ?) x$ P w. _/ s

)public RespResult logout() { 4 H- ^3 M* d* D3 ~# L& I4 D1 j5 m // JSONObject payload = JwtUtils.getJwtPayload();// String jti = payload.getStr(SecurityConstants.JWT_JTI); // JWT唯一标识

+ q3 C. n2 }8 ?7 z4 e

// Long expireTime = payload.getLong(SecurityConstants.JWT_EXP); // JWT过期时间戳(单位:秒)// if (expireTime != null) {

/ m7 \8 Y8 W* T0 W8 |% x" V

// long currentTime = System.currentTimeMillis() / 1000;// 当前时间(单位:秒)// if (expireTime > currentTime) { // token未过期,添加至缓存作为黑名单限制访问,缓存时间为token过期剩余时间

! ]$ d2 b) u* x* @

// redisTemplate.opsForValue().set(SecurityConstants.TOKEN_BLACKLIST_PREFIX + jti, null, (expireTime - currentTime), TimeUnit.SECONDS);

v& K5 ? q) P. A

// }// } else { // token 永不过期则永久加入黑名单// redisTemplate.opsForValue().set(SecurityConstants.TOKEN_BLACKLIST_PREFIX + jti, null);

! f H. h6 l0 V- a @4 C

// }// return Result.success("注销成功");return RespUtils.success(); 0 l. c6 ]( v" ~: X( k3 K } , }0 I; W; Y+ @" d6 z# g' o }补充:授权码模式获取access_token

8 P0 { S2 `1 q; F+ H

菜单登录以后,前端会查询菜单并展示,下面是菜单相关接口 SysMenuControllerpackage com.soa.supervision.uaa.controller;* T3 s5 t/ B4 ?0 ~' X 4 ?5 D6 g( M2 l- Y1 J3 F! O" P B: _% Q. U* {' \1 e import com.soa.supervision.uaa.domain.MenuVO;$ ^2 K6 E7 [, v

: Y( X# C6 F0 j& v

import com.soa.supervision.uaa.service.SysMenuService;: u0 J2 N% `6 G# m; w# f8 l import com.tgf.common.domain.RespResult; ( `# c0 i V8 j, [( ~" D; D import

; { o. \! Q1 |1 X

com.tgf.common.util.RespUtils; - Q; Q i! Y3 R5 E& S import org.springframework.beans.factory.annotation.Autowired;# N* ]) M" `/ R& y7 S% @* H; A! J& Q import

# j* w3 e* H' _7 G: z1 h; f2 j1 k

org.springframework.web.bind.annotation.GetMapping;/ Z/ w$ R$ |! V: K import org.springframework.web.bind.annotation.RequestMapping; 6 M+ I1 N W5 L$ p / v: @* {' b5 ]2 P& n% z2 u( t1 D

! E0 h5 b1 J0 J f8 K

import org.springframework.web.bind.annotation.RestController; + w) |, @; y' e/ J6 y. @ % Q- h/ Y! Q/ ^9 v import java.util.Arrays;/ I" P1 m5 l. K. h+ m; I import java.util.List;8 n7 \0 W1 Z# Z8 E* a ' J: l9 p w& p4 t1 D( ]

% [4 D! Q6 i. k6 Z( d' H0 V3 r

/**! Z* W! R8 J; f *

- W% E2 T% U4 a! p6 T K5 V

, b% \/ t4 ?: D. H3 ~ w3 s2 \5 m2 t * 菜单表 前端控制器2 T" L5 Z8 C* D1 ?5 n *

1 @5 V: @! j) `6 {1 y * $ G0 u9 A" d+ O, b% _6 R * @author ChengJianSheng + `1 I0 m. o# q- \8 G * @since 2022-02-211 w4 A: Q) K- y8 l$ I2 X$ A' Y */@RestController@RequestMapping(

"/menu")publicclassSysMenuController{ 2 Q4 H8 d! G+ H- {/ Z0 ?, I5 Y: ^6 l* S, B: C5 n @Autowiredprivate SysMenuService sysMenuService; / N: t3 T* L$ Y$ U. r; @: }" R( z7 } @GetMapping(

# G/ v* h8 M8 Z4 S! ]6 h$ y

"/tree")public RespResult tree(String systemCode) {5 ]) P+ ~- [9 Q# b( `# Y List roleIds = Arrays.asList(1,2

- N& v1 [) n; p

); - K7 R+ I1 G) u5 o: B List voList = sysMenuService.getMenuByUserRoles(systemCode, roleIds); M0 s/ m9 h- M9 G9 U8 O return

% N& v" b5 l1 ?- f6 e

RespUtils.success(voList); " f' t8 k9 z) y4 e }& P; b( v0 l8 l- \% ~& S( j) [+ U }SysMenuServicepackage com.soa.supervision.uaa.service; # d& B* @" J* I! u! |) d# ^# T6 d5 U" f & |9 b( K$ I2 E; _* q* f import com.soa.supervision.uaa.domain.MenuVO; - ]) ], t/ j7 {

0 c! p, p6 W# x7 N( I9 F5 Y

import com.soa.supervision.uaa.entity.SysMenu;; y1 O- ]* y) e# g# N, ]6 ?0 f Y import com.baomidou.mybatisplus.extension.service.IService;+ u' \2 A+ J* ^% T* x: S " |- x7 e9 q- u$ J

9 c3 i% _; O2 r- k) p) N1 _& R

import java.util.List;: ^( J S3 _ Y$ b: P ; Y9 P) y4 J6 b9 {9 c+ P /**! ?2 I* U4 O( w o8 x9 G *

4 q3 Q/ s1 v2 q8 W

2 n2 H# v; u( r * 菜单表 服务类 2 Q& j" o3 e$ {: F. R! G5 l *

4 s% e0 m( l8 ]- F9 T5 Q# ?0 k7 q * . }* s0 E! Q: u' a * @author ChengJianSheng , T' d% |2 p' k+ U * @since 2022-02-21 1 C9 ~" I$ a' k1 M1 e r0 B */

publicinterfaceSysMenuServiceextendsIService {1 b! n* ]6 T! w7 M( T" T List getMenuByUserRoles(String systemCode, List roleIds)

8 O; J! ^; S1 e

; 9 y( `. C! w) D* L& Y }SysMenuServiceImplpackage com.soa.supervision.uaa.service.impl; - v T, V. u, v3 ?( l s5 } ; N3 x# ?- i+ w( H% l import com.soa.supervision.uaa.domain.MenuVO; ( b' x9 P& X# P3 v3 M% A1 X; i0 ?

9 O. X4 m% j3 ^3 n3 x% ]

import com.soa.supervision.uaa.entity.SysMenu; g1 [# ]. Z2 b0 y( J/ Q import com.soa.supervision.uaa.mapper.SysMenuMapper;5 i, U$ ], ^* J5 K; [0 R, D import

! ]9 m; r& J9 O2 k

com.soa.supervision.uaa.service.SysMenuService;: j+ m! r. ]( D" Q; R import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;7 H V4 u0 d. I2 p' y! p; S

9 U/ Q% s/ t# c5 R' A6 A4 Y

import org.springframework.beans.BeanUtils; ! h" P t9 ~8 m1 V1 d$ h- M import org.springframework.beans.factory.annotation.Autowired; 7 O9 P1 C m/ U3 U9 P; K8 \

, b- @' y" H s3 K

import org.springframework.stereotype.Service; 7 r' Z- w. _$ ~1 B' K$ m , w% c1 F" h3 R; H import java.util.ArrayList; ! ~0 H- e8 G8 o$ z c6 e k, r import java.util.List;+ E$ R- @/ T) }# @: t import

6 a( C& X1 Y2 {9 v1 S

java.util.stream.Collectors; @7 Y' w- G; m2 R$ x- {) N6 g2 k0 P$ M, L0 t* ^* B/ B /**4 _- ^. R% P6 Y! `5 m *

: d. T8 |2 l& {- P( T

8 B2 b2 h2 _0 W! m _ * 菜单表 服务实现类 6 @/ f3 E7 G% S/ J. V *

) b; g7 K3 w! g) \ *4 Z' Y8 h) `5 v; l- e9 @ * @author ChengJianSheng$ v( N2 n* A4 |# ?9 I v" ^( d * @since

2022-02-21 4 D" d8 }1 l" f3 Y2 B* s1 m$ {+ t */@ServicepublicclassSysMenuServiceImplextendsServiceImpl implements

) k& g4 J' y4 [; `/ b

SysMenuService{2 f, _" l, k! L, S 4 n& F0 ^6 v5 U3 H @Autowiredprivate SysMenuMapper sysMenuMapper; 4 z }" A6 @- K! n1 T& L4 V7 z/ m) S1 ^1 h, H6 d; q% D /** ! O9 T* k! i: D- J5 ~& f * 构造菜单树" j) @5 s9 h" n& G/ X4 A * @param

! W7 w. d# R ]& h# P

systemCode9 A' z* `9 V! n9 M3 Z. k4 V * @param roleIds' p4 M" O% r' H% b * @return+ K! x( ?+ U5 X# Z2 Y, Z, D: | */@Overridepublic List getMenuByUserRoles(String systemCode, List roleIds) { ( F4 t7 u0 B2 U8 i5 ~2 X6 [9 Y( d List voList = new ArrayList<>(); ; `; o" U: u( L8 B) T: m: l7 C6 O: ^. c List sysMenuList = sysMenuMapper.selectMenuByRole(systemCode, roleIds);8 p2 R8 \9 [3 d! ~7 D

# x, {2 {: Q% v% I+ N; w+ R

if (null == sysMenuList || sysMenuList.size() == 0) { & H( z" L. v# o3 s6 x return voList; 1 O" ?7 E! E; [9 P8 [ }, c6 X$ I" D2 W1 _$ H List menuVOList = sysMenuList.stream().map(e->{( K1 j4 k3 r1 N% m0 \+ _ MenuVO vo = new MenuVO(); 7 \( X% t _' B3 Z- s& a9 i BeanUtils.copyProperties(e, vo); 8 Z# z. d- @* P4 A! X vo.setChildren(new ArrayList<>()); 6 g8 D3 \4 c1 |7 H( x# \4 }

0 \6 x2 G( o- b4 J# {

return vo;5 C0 Y: C" K8 K9 @( T1 A8 J }).distinct().collect(Collectors.toList()); 7 {: K9 w: ]9 U7 Q* k ' @9 D: n1 X0 s$ g5 A for (int i = 0; i < menuVOList.size(); i++) { 2 a, P9 w- k, T! B) p |8 M' b

: v, V* N5 R: t3 ?

for (int j = 0; j < menuVOList.size(); j++) { ! j2 k: R% t6 C6 C3 K9 s- k" }% V if (menuVOList.get(i).getId().equals(menuVOList.

. ]7 {* d, i' G, @; i" V0 u

get(j).getId())) { 0 E" [, Y) G% q/ n+ F continue;' p) F" p- M- `! m. M } 4 }0 d+ \6 L3 j B: g' f if (menuVOList.get

8 y9 Z4 @' z# L) j; w

(i).getId().equals(menuVOList.get(j).getParentId())) { ~3 u% F$ c, |4 v4 p: @ menuVOList.get(i).getChildren().add(menuVOList.

1 p B5 t0 ~$ p2 n/ y

get(j));8 A+ L* U m- r5 X8 E3 B/ q } l3 B5 p, z; T/ F }3 D% V9 i- z* `8 O( z( [! ^: y } 5 o) v) t% m" R: H! V% y, f( D0 R! Z& j0 K. o, H return menuVOList.stream().filter(e->0==e.getParentId()).collect(Collectors.toList());! n' {* _1 q1 R' M' k$ x } $ S% L6 ?* N* G* b }

7 v/ `. V& k7 L

MenuVOpackage com.soa.supervision.uaa.domain;3 E& b/ Y- d- h$ e* u( u' O n) q E8 V, P* Z, S1 c, b* Q import lombok.Data;! ]2 I- s! d) o7 T ' T9 F6 J: s9 d2 ]' r import java.io.Serializable; ' j- ~0 N0 \& a8 M8 c/ b import

: {) g6 l& f& |4 m6 K9 h& k% V

java.util.List; 2 |, `( X0 ?& r9 x; \: s; u' g% I" W /**0 r% j' R% e7 p* S0 v8 c4 t * @Author ChengJianSheng- n5 \6 s0 Y2 P7 d! v1 L) e * @Date 2022/2/21 $ y$ f3 k8 ^! F; u8 f* p% [ */@Datapublicclass MenuVO implements

5 h" h0 u6 h* g$ d5 U# x; R

Serializable { y$ X; W" i4 \% v. y& O0 U- a9 B ) Z- u+ K7 ?: b$ l) x B private Integer id;7 c. P; \7 X! B _: I5 e0 F# C1 \ 8 H# F3 z4 W- N9 U# N /** 7 k( F7 a* i4 l) o8 R * 菜单名称 . |- _) g% \) v4 M */privateString name;/ G8 t. n3 j9 N5 p8 s # u" t# F8 x! i- S7 ] /**: @/ A$ @! ?: q6 r * 父级菜单ID # P, f( B; N' C3 Z0 f */

' { `( N; k2 W* v. G8 _

private Integer parentId;* P% `- c" ?3 l& q: L( m & S# I3 O( n; o /** ; R T* [6 d, l0 V/ Q * 路由地址 2 A) h8 i1 K" T* a */privateString routePath; e# P# p& q7 X8 _% F8 R+ n0 h T% n9 O7 {% B) J) F# v /** # u) Q ~! ]$ Z/ E3 h * 组件 2 y( B) D" H" M2 G# K1 R */

' t0 G5 B0 g% M: _- t, y2 X) ~& H

privateString component; % i- `4 p% A1 {5 t0 u ! F' r |; d& k& t% [5 Z8 ] /**. [. V2 z! X- ~1 A) j- C * 图标 : g2 \6 x# C$ D$ ^4 @ */privateString icon; ' C) ^0 R4 f& T( _4 u% y ! E; x( \1 |* ]! R4 f+ A4 ~, x7 Y /** + v0 A$ t. ~+ B5 K * 排序号 ( K; l7 I- s2 a */private

$ N4 x, [# t+ j6 y, U8 p' A8 P7 Z

Integer sort; 8 j' c$ F' Z2 B; t2 v' j' L. d$ } C( j; y7 o3 F- ]2 g* t6 p /** , H7 z0 x3 @) P: d4 o9 X; ? * 子菜单 . A6 Y, u: M, I" i4 X$ S */private List children;9 m m4 Y) m1 _: l: R- E }SysMenuMapperpackage com.soa.supervision.uaa.mapper; u Y2 z! Q6 Z1 C ! C9 s' C" o8 ]) M0 h

/ D: w9 e) i/ U1 u" u

import com.soa.supervision.uaa.entity.SysMenu;3 l% P3 Y- t0 ~1 Z, p& R8 S2 Q+ P import com.baomidou.mybatisplus.core.mapper.BaseMapper; ( o% i6 [, a5 S1 {8 U% n9 a

, [0 X9 @: x8 r# a, U2 X- b8 I

import org.apache.ibatis.annotations.Param; 7 T1 @, u/ ^- i ; ^3 b# a" W! c) u, @& r import java.util.List; 2 R* V* a- n/ L1 Z9 M& l1 P- R3 y6 N$ ^( i, z& e /**& X* d a5 E7 c' m& t F3 X *

# {5 x5 q5 ]6 R- ]' P% c/ I

! ?( M' q, q! V" Z * 菜单表 Mapper 接口, H% n3 [/ F5 a5 |" I: i *

8 A* @4 {$ X8 _) S, s * ; y/ S5 R* O) A( b8 g# l. A *

@author ChengJianSheng/ l; G; P. V$ S( x7 s/ N2 K0 A' v5 v * @since 2022-02-21 2 Q1 f* Q8 y% W */publicinterfaceSysMenuMapperextendsBaseMapper

6 D: ~2 }/ E) v; @- f( E

{) s/ B j8 ]0 g ~1 Z List selectMenuByRole(@Param("systemCode") String systemCode, @Param("roleIds") List roleIds); 4 x. x! R/ Q% M& n# K* i+ ?. k! _ }

4 \( Y0 d# a, z- A

SysMenuMapper.xml

; l2 [9 b" j4 o

"http://mybatis.org/dtd/mybatis-3-mapper.dtd">

V6 R/ c2 e8 a# Z% v

> 6 j- z: R+ y. Y! x& z# h } SELECT4 ^* t( \% L7 g/ d) l. b t1.* ! h0 o2 i7 b H0 l FROM ! H+ p# `9 {' Z2 [1 j sys_menu t1 # [7 x* V. X% d; ] LEFT JOIN sys_role_menu t2 ON t1.id = t2.menu_id+ V: V5 J, |, g3 U+ ]2 u WHERE5 k. q2 D% v/ p- U# P t1.system_code = #{systemCode}3 o& U, h1 H' Z AND t1.hidden = 0 . d& c; m( q0 D& O, w) i AND t2.role_id IN

2 b |; a3 G, E" q& ^" f; O

#{roleId} 8 u' j# e7 N' |8 y ORDER BY9 b4 C" S' F, A t1.sort ASC 6 x9 G. y1 G, H* d: b' i

7 i; c: H8 u3 ?/ c# A

application.ymlserver:port:8094servlet:context-path:/soas-uaaspring:application:name:

2 A9 _* G6 J6 c

soas-uaadatasource:url:jdbc:mysql://192.168.28.22:3306/demo?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf8&useSSL=false

5 V+ S b9 E. [1 y I

driver-class-name:com.mysql.cj.jdbc.Driverusername:rootpassword:1234567redis:host:192.168.28.01port:6379

% i& [3 v/ D. e. i+ R/ N

password:123456logging:levelrg:springframework:security:debugmybatis-plus:configuration:log-implrg.apache.ibatis.logging.stdout.StdOutImpl

, @; ~2 M3 A4 K) K/ D

3 网关在这里,网关相当于OAuth2中的资源服务器这么个角色网关代理了所有的业务微服务,如果说那些业务服务是资源的,那么网关就是资源的集合,访问网关就是访问资源,访问资源就要先认证再授权才能访问同时,网关又相当于一个公共方法,因此在这里做鉴权是比较合适的。

: A% N; Q" c/ T

首先是依赖

" l1 s/ f2 N! q1 }

="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"

. |* n$ q8 R, G+ y( D3 a

>4.0.0com.tgftgf-service-parent & T% p+ b5 f' | V: L* Y. S ( G: H( x" F3 C/ W8 s6 ?

* ]4 u3 J- j- E& H& G - d# Y" w `6 h4 n/ M, @* \' D$ @6 Z# s+ w. i/ a a" \

暂时无法加载帖子列表

回复

使用道具 举报

幸福的蚂蚁 发表于 2026-10-09 14:53:05 | 显示全部楼层
这个思路很新颖,打开了新世界的大门,谢谢分享
回复

使用道具 举报

    您需要登录后才可以回帖 登录 | 加入怎通

    本版积分规则

    QQ|手机版|小黑屋|网站地图|真牛社区 ( 苏ICP备2023040716号-2 )

    GMT+8, 2026-11-1 04:13 , Processed in 0.083484 second(s), 24 queries , Gzip On.

    免责声明:本站信息来自互联网,本站不对其内容真实性负责,如有侵权等情况请联系420897364#qq.com(把#换成@)删除。

    Powered by Discuz! X3.5

    快速回复 返回顶部 返回列表