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

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

[复制链接]
我来看看 发表于 2023-03-06 21:36:40 | 显示全部楼层 |阅读模式
, p7 d( h1 J+ y: {8 A

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

1 U }3 t3 H( X, q% I+ F

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

8 \8 L- v* i$ I

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

, n. b) i2 ]7 w5 @+ @. D

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

1 W3 D4 S O; }- w, L4 e& k

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

2 ?$ S7 P' V7 V8 ]

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

, v$ Y: L$ z0 t' B4 R

modelVersion>4.0.0com.tgftgf-service-parent

6 }0 \8 L: l, a# C) ]

>1.3.0com.soa.supervision.uaa

7 q' _, C1 @, q& v# o3 P

soas-uaa0.0.1-SNAPSHOTsoas-uaa

2 c1 U5 Z" Y2 [* {: Z. o6 F; u: h

>1.82021.0.0

% y- B2 `2 Y- l R

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

. v _- \1 o: ^8 s

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

D( f) O& |) {) K' z

org.springframework.cloudspring-cloud-starter

& L6 Z& X3 K! u- @5 d! |7 G/ |

org.springframework.cloudspring-cloud-starter-oauth2

- U( h5 U4 I m/ @' p, y! k

2.2.5.RELEASEcom.nimbusds

0 Y" Z1 r8 b( F4 A2 m6 \3 Y

nimbus-jose-jwt9.19com.baomidou

9 V8 r: i+ |. d: | K

mybatis-plus-boot-startercom.baomidou

* q2 ?; B% c/ D! V1 P

mybatis-plus-generator3.5.1

5 v$ O F+ Q' x5 a. r

>org.mybatis.scriptingmybatis-freemarker1.2.3

5 ~& ~9 ^9 b2 m; x. `) Q% G

>mysqlmysql-connector-java

; T( I5 _7 Y s. C( }( x6 J

>runtimeorg.projectlomboklombok

7 H- l7 F4 m1 s0 G2 I9 P+ H3 m1 X' X

>trueorg.springframework.boot

! _7 A) S" V/ x/ R

>spring-boot-starter-testtest

) q+ B9 ]! `2 z4 K1 I

>org.springframework.cloudspring-cloud-dependencies

2 D% n) r" _' C3 a

${spring-cloud.version}pomimport

0 e# W# }- z$ n6 G" C0 W

>org.springframework.boot

$ \1 T6 a z- W* y: n( F; Z% n

>spring-boot-maven-pluginorg.projectlombok

, o1 i, a! m7 s& p9 L" {# O& j

lombok

7 ?3 E% x8 J& M* a2 B h2 A

>配置授权服务器在授权服务器中,主要是配置如何生成Token,以及注册的客户端有哪些package com.soa.supervision.uaa.config; 4 l% b2 A, E, w3 o, N2 l/ y " }. z2 H" \ x! ^ import com.soa.supervision.uaa.constant.AuthConstants; + p$ X, n1 n. `" |

6 f$ ^! `4 H! \4 |7 e/ L

import com.soa.supervision.uaa.domain.SecurityUser; + {( A# Y$ ]7 S$ N' T8 o+ k import org.springframework.beans.factory.annotation.Autowired; X7 u; j7 |! T/ K, n0 B

3 Z8 |- a* ]- ^- z8 J* U5 v

import org.springframework.context.annotation.Bean;+ E9 R" s* ^# ]) b* w import org.springframework.context.annotation.Configuration; % E8 L: d9 s6 W

& C: s% t! N% z! g6 J

import org.springframework.core.io.ClassPathResource;: G; {1 v& }# {2 k import org.springframework.security.authentication.AuthenticationManager;* \& W* `1 D; B0 c( `

: \+ r/ B0 e( b+ y* c! j+ K+ V

import org.springframework.security.oauth2.common.DefaultOAuth2AccessToken;7 U: B: { g+ p* G; f1 o import org.springframework.security.oauth2.common.OAuth2AccessToken; ; a- E' I; C$ o! D+ N5 Y* o/ q

9 R+ K7 n) X) q) [% l

import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer; % q5 r: S% V! j' ]/ m

8 M( I! |, h: t

import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;: @- z1 j& I- s' g7 w

( {( g: R0 O- D z2 x' L% D: Q

import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;. r3 s) ?5 f# A6 c! j* h8 G) B

) X* i5 }4 Z$ b, Z a. I

import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer; 8 t4 C0 a [ |5 }

3 m6 S3 ?( _2 c* O

import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;7 J4 B6 a K. ?6 b: K% d

8 t- T( {3 }1 d$ K- j; |/ _: i

import org.springframework.security.oauth2.provider.OAuth2Authentication; 1 L6 b! A( K- @& I import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService; 4 x& \) I5 o! B8 r8 O6 r Z; H

7 O k5 I% n$ P' M& W/ |1 i

import org.springframework.security.oauth2.provider.endpoint.TokenKeyEndpoint;& m5 c' H1 q& l/ A5 P import org.springframework.security.oauth2.provider.token.TokenEnhancer;6 E! ]% N+ ^, ^4 v Y

( N2 Y3 W- R+ q) ]9 _4 p

import org.springframework.security.oauth2.provider.token.TokenEnhancerChain;0 L7 W9 k; R$ F3 m) m, u import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter; 6 h5 G! i/ V, D, i- D# G

5 J* [8 ?4 @6 y8 P

import org.springframework.security.oauth2.provider.token.store.KeyStoreKeyFactory;/ O6 b$ \& i$ h) @$ S ; `8 r: t4 }& f' z import javax.annotation.Resource; & F. a" u4 j. K. d3 u$ G

$ R3 ~ G* _( E0 v4 t7 X0 {, g

import javax.sql.DataSource;) o% P# e7 U, h9 O* W import java.security.KeyPair; % s( E- Q( M5 q; m$ C, [& o import java.util.ArrayList; ! c7 w3 y9 z* N1 U import java.util.HashMap; 1 e3 v1 [) ]( P2 m' V' x

, F* D9 S- a+ |! ~2 e

import java.util.List; ) } h" L8 E# a( y1 b, y import java.util.Map;$ P/ B9 K1 S* n: b ' a3 N& B( W, M8 {2 q /*** }! K, b( T: _4 F * 授权服务器配置 8 c' s) Q/ R: M3 x7 m$ L* l0 p * 1、配置客户端 3 P! E p1 e: j. W * 2、配置Access_Token生成 ( h( T8 L. g; S *$ B- J- a* R' W1 {/ D * @Author

/ \$ v; D1 s' @/ p' i$ ?5 M, S0 ?4 r

ChengJianSheng 2 n* |& P3 O* S * @Date 2022/2/14 / N& g( u, ^' R% f0 _ */@Configuration@EnableAuthorizationServerpublicclassAuthorizationServerConfig

1 f2 V* ]2 D6 ^' ^3 z

extendsAuthorizationServerConfigurerAdapter{9 m! J: \) v: G+ p5 D ; K4 H' C" E. q9 Q @Resourceprivate DataSource dataSource; f( Y3 |0 x: n* \- g6 k8 ]) m4 g @Autowired

3 v% @, \& _2 n' y) c5 P9 L1 C- X

private AuthenticationManager authenticationManager; 8 p) E) g! v4 I: E+ W$ O 3 y$ J8 ^ P8 l! M# c! ?! `7 F7 O# D @Overridepublicvoidconfigure(ClientDetailsServiceConfigurer clients)

; u* T4 ~' o T9 Q% i7 T

throws Exception { % \0 s6 j& p0 j9 C! G clients.withClientDetails(new JdbcClientDetailsService(dataSource)); 6 k! E* H! @6 d$ s- ?9 ` }5 \* Y" y8 i' C( S& a: B4 R 7 w( ?0 C0 Q- F+ X6 B8 e

& T/ H) G8 p6 ~1 N0 p

@Overridepublicvoidconfigure(AuthorizationServerSecurityConfigurer security)throws Exception { + O2 W# I+ w/ x" P1 y7 T7 q security.allowFormAuthenticationForClients();% i1 C/ m2 S+ u& I' S+ R4 E. ?

; [0 F; D- U9 h2 ]8 z+ a

// security.tokenKeyAccess("permitAll()");$ f" _5 s# R h5 c } * ~" C, |: B* B; l$ s; [: ]" Z) _6 k* d; @" ~" [6 j @Overridepublicvoidconfigure(AuthorizationServerEndpointsConfigurer endpoints)

7 i3 T1 y, N: c

throws Exception {$ H9 r- h; |- ^, w2 ]5 ~ `# A8 j) @ List tokenEnhancerList = new ArrayList<>(); 5 P( H5 D8 ^: h( @. s tokenEnhancerList.add(jwtTokenEnhancer()); # e' U. n" \# b2 w( {9 R/ s tokenEnhancerList.add(jwtAccessTokenConverter());- u" b0 ~* w9 ^' ]. J# _0 j5 K# G* } U TokenEnhancerChain tokenEnhancerChain =

, l! L# c0 y% ]- T5 ]1 P2 E# P7 C2 a

new TokenEnhancerChain(); 6 {# L7 l$ N3 P' L2 g; @0 ~ tokenEnhancerChain.setTokenEnhancers(tokenEnhancerList); 8 X1 E j$ U; |0 M- e$ d# s1 Z- }* K/ i- o% I* C endpoints.accessTokenConverter(jwtAccessTokenConverter()) 5 `- h8 T z( @1 N: W .tokenEnhancer(tokenEnhancerChain).authenticationManager(authenticationManager); 5 s2 y- R; P5 e5 b2 D! k& Q } 2 ^3 M- h* k+ t/ \/ v6 n # R, C( X( r% J1 c3 o2 ]6 D

& d9 |3 P R) y( g* W

/** 1 L: f$ }# f* C/ f' o% K * Token增强2 _7 g, X( G% x7 ?6 l2 j1 Z */public TokenEnhancer jwtTokenEnhancer(){ * I8 I+ q, r+ R) j returnnew TokenEnhancer() { 0 U& ]5 x, N0 r, d; ]

) }% |7 e- L, {. t L8 M

@Overridepublic OAuth2AccessToken enhance(OAuth2AccessToken accessToken, OAuth2Authentication authentication)

3 T F3 O3 |# |1 g6 `1 T, X

{! [6 c: L4 C' q1 t, E# v0 x8 [ SecurityUser securityUser = (SecurityUser) authentication.getPrincipal(); : f6 V9 ?6 i3 h! k$ _, I6 J0 V Map additionalInformation =

- ~# X) a7 i ^: t' K& u# K

new HashMap<>(); # J8 U# ~0 Q& Q9 f, G additionalInformation.put(AuthConstants.JWT_USER_ID_KEY, securityUser.getUserId()); y! ]; `: E I$ w. X2 C( e4 x$ x additionalInformation.put(AuthConstants.JWT_USER_NAME_KEY, securityUser.getUsername());; Y0 T# X" J4 S3 n8 Y% { C- s additionalInformation.put(AuthConstants.JWT_DEPT_ID_KEY, securityUser.getDeptId());1 R+ P7 I7 h3 h' v ((DefaultOAuth2AccessToken)accessToken).setAdditionalInformation(additionalInformation); + b& z; X/ `- A% u. @9 J0 e

. d# P% O* |2 A0 {/ O" M# P+ ?; b

return accessToken;0 Q* B+ z& y7 [+ g- h& k3 F% K } , l1 P+ U+ U' i$ _3 P: J }; ) z/ j5 P4 L8 T( \: W }' S- d7 T* c6 z+ u0 e# m( q5 H 4 ?! S% J3 X) D% ?0 u- V- C' G' z /** ! i5 e! ` P& O! D * 采用RSA加密算法对JWT进行签名 : l: j/ a1 `+ b */public JwtAccessTokenConverter

. Z! e/ g/ }! W/ y- G

jwtAccessTokenConverter(){ . |0 i3 ~. @/ T5 {& } JwtAccessTokenConverter jwtAccessTokenConverter = new JwtAccessTokenConverter(); 3 c2 {4 j, C' T6 j jwtAccessTokenConverter.setKeyPair(keyPair());6 [" | [& h4 ]; ^- Z& j

$ I7 F7 O/ s$ [/ [7 Q

return jwtAccessTokenConverter;$ ]5 Y! `' h5 h8 K' u$ z }0 C. U4 Q- y3 V8 U) h# a: f 2 [3 Z! a2 p/ Y/ H9 e /** ' H! M( C" x n * 密钥对 6 F/ N- d; }8 e8 N8 i2 ^3 l */@Beanpublic KeyPair keyPair(){ 0 v. h- X3 X2 T- `0 J' x' J KeyStoreKeyFactory keyStoreKeyFactory =

- H' ?* I$ W4 q5 k6 }* Z! q3 \

new KeyStoreKeyFactory(new ClassPathResource("jwt.jks"), "123456".toCharArray()); 4 l. w- I' _6 l3 u return keyStoreKeyFactory.getKeyPair(

/ Z- i% T9 Z6 l# c

"jwt", "123456".toCharArray());4 X4 \5 K, T/ [" S& o* s }" s: j0 H5 t/ X+ U* O9 R 5 _: l8 I8 J2 s6 @, I0 z7 p$ E @Beanpublic TokenKeyEndpoint tokenKeyEndpoint(){/ ^% l, V# [; Y* x# f1 q0 y4 R return

+ }( K/ r4 Y. Z' w# w

new TokenKeyEndpoint(jwtAccessTokenConverter()); 3 _& c7 G0 ^1 i5 } }/ Z6 X. Y0 O3 i2 s1 T, T$ ` }说明:客户端是从数据库加载的密码模式下必须设置一个AuthenticationManager

5 T. w8 l Q8 [' n5 p9 ~

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

& \' R" F8 e1 H9 X

此处对token进行了增强,在token中加了几个字段分别表示用户ID和部门ID客户端表结构如下:DROPTABLEIFEXISTS`oauth_client_details`; 6 h3 Z% }) N- a6 ^! { CREATETABLE

5 e3 R- p1 K. f6 G

`oauth_client_details` (% A( E4 x b3 ?2 X. t `client_id`varchar(256) CHARACTERSET utf8 COLLATE utf8_general_ci NOTNULL

* w* d) n# _9 ]" l

COMMENT客户端ID,9 W: F$ d8 C9 P% E! O0 b7 s `resource_ids`varchar(256) CHARACTERSET utf8 COLLATE utf8_general_ci NULLDEFAULTNULL, : _6 ?( ?7 G- E4 \, q

! b3 I' q" x' g3 t! z

`client_secret`varchar(256) CHARACTERSET utf8 COLLATE utf8_general_ci NULLDEFAULTNULLCOMMENT客户端密钥," s, h9 z1 V" H8 @* Y

/ M6 m, F" P. U3 ]4 v" I( W

`scope`varchar(256) CHARACTERSET utf8 COLLATE utf8_general_ci NULLDEFAULTNULL, ; P# z8 a# v. G/ G+ M `authorized_grant_types`

- Q5 R" G( o& v2 S% \9 h) p

varchar(256) CHARACTERSET utf8 COLLATE utf8_general_ci NULLDEFAULTNULLCOMMENT授权类型, 3 u0 a6 N5 l6 L1 @ `web_server_redirect_uri`

8 C' S- J/ I6 E: I# U/ w

varchar(256) CHARACTERSET utf8 COLLATE utf8_general_ci NULLDEFAULTNULL, ?! o8 k" l* [) H `authorities`varchar(256) CHARACTER

+ h7 i( u2 z3 Z! M" F1 f

SET utf8 COLLATE utf8_general_ci NULLDEFAULTNULL, X8 F$ G6 ~7 [; B# u9 S/ a `access_token_validity`int(11) NULLDEFAULTNULLCOMMENT

, F3 t- b( _0 O. U. a+ C

access_token的有效时间, % w1 g$ s) ?% D0 W `refresh_token_validity`int(11) NULLDEFAULTNULLCOMMENTrefresh_token的有效时间,4 Q" x" `* E! u9 q$ s `additional_information`

# \, a3 f( ~# V, l1 @7 \& r

varchar(4096) CHARACTERSET utf8 COLLATE utf8_general_ci NULLDEFAULTNULL, 8 z& ] w/ M7 [+ T$ `" o `autoapprove`varchar(256)

. f9 _. m. [1 Y. I7 ^

CHARACTERSET utf8 COLLATE utf8_general_ci NULLDEFAULTNULLCOMMENT是否允许自动授权,$ A1 n! x1 a) G9 ` PRIMARY KEY (`client_id`)

3 e+ Y4 \! m: v" _6 r1 q7 y. Q" g

USING BTREE 6 Y/ h5 x/ ^% w2 ]. L ) ENGINE = InnoDBCHARACTERSET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = DYNAMIC;7 f% T. {/ `# n# s) a & a3 A3 k3 ]2 }' n0 I0 k; z5 N INSERT

+ {9 }( G( p# P% a& C

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

8 f9 q/ ?+ u; Z7 n* ~7 Q

, all, authorization_code,password,refresh_token, http://www.baidu.com, NULL, 7200, 7260, NULL, true);% O! i* C5 |2 G

/ Z8 l! B9 P% c- H+ w9 C! N; X

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

3 N+ h: P z' h ?0 Z7 a7 H

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

1 K, Q4 r3 W l, J! Q

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

, }" j) l# t ^2 W& a- b

将来,认证服务器用私钥对token加密,然后将公钥公开package com.soa.supervision.uaa.controller; 3 B5 q' H6 d# g" {( b . t7 d* a% @8 e7 [! }2 h! v import com.nimbusds.jose.jwk.JWKSet; R# Y) p# p- ~6 r' M, @

0 e& |" p# w3 B

import com.nimbusds.jose.jwk.RSAKey; ( d Q1 X t& Y' r% O, C import org.springframework.beans.factory.annotation.Autowired; & M/ z+ j/ |0 r/ C- n- R# G import

6 e$ B! @; e' g# L- M& t/ D

org.springframework.web.bind.annotation.GetMapping; 7 Z% ]" x+ {+ p' J* S" p import org.springframework.web.bind.annotation.RestController;# I; @; @" w, B! m. z9 h8 E ! C1 J" J, K9 v$ |& ?6 o

/ e% w& W0 O3 b

import java.security.KeyPair; # U" `' F) q, W( e import java.security.interfaces.RSAPublicKey; . _2 g% w7 w" K" Z' B0 S2 X( {2 u import java.util.Map; ; {; A6 w" G, `& m. j2 X* @ O1 n4 X5 H4 ` /**# T9 W* W. g2 F+ n9 p* D! H *

$ V( M/ R# D. S% T+ q

@Author ChengJianSheng* I2 V* o6 s* R2 q; \- n% t, f * @Date 2022/2/15. u( P& G. C- q9 Q+ Y! x2 W */@RestControllerpublicclassKeyPairController{& K% D- v1 v2 \) e9 O% O& |' s9 Y N: F& d9 V! X @Autowired

4 @6 g. I& [+ ^! q9 h

private KeyPair keyPair;9 ]" O! R/ L; z/ d' U) O9 \4 R! U: p: m 6 a. D# K+ O5 D; n5 ~1 q @GetMapping("/rsa/publicKey")public Map getKey() {7 q4 O9 a# p7 A h+ Z' U2 Z RSAPublicKey publicKey = (RSAPublicKey)

' ~" Q" Z6 L% z" g5 ?

this.keyPair.getPublic();/ E1 z* a" }' {8 Z; l7 K' j1 { RSAKey key = new RSAKey.Builder(publicKey).build();* Q/ w5 R5 u3 j return new JWKSet(key).toJSONObject(); % T* ^$ Q2 @( _# l4 I/ x6 ~2 ? } # v+ M6 H, k$ ]; B& Y }

. y7 {8 E# F. f# E

配置WebSecurity在WebSecurity中主要是配置用户,以及哪些请求需要认证以后才能访问package com.soa.supervision.uaa.config;# H& ~3 ]% e* J, u: S( {: f% ^4 A " r: \; ^: A6 z" } import com.soa.supervision.uaa.service.impl.UserDetailsServiceImpl;6 v% j, R; X h' p0 c* w' e* _- q

6 |4 i" d: t) D4 m V G0 U; k3 x/ N

import org.springframework.beans.factory.annotation.Autowired; ' h# {. B1 B) i3 q% a6 ~- p import org.springframework.boot.actuate.autoconfigure.security.servlet.EndpointRequest; : x5 N2 | M5 [; i3 W

( c7 r+ p* r- |6 L; S

import org.springframework.context.annotation.Bean; 0 c% @. C0 J- G+ i- v" ` import org.springframework.context.annotation.Configuration; ) {" t4 w/ Z& Y, ?' t2 t% U# R

- y2 q3 L3 J, z5 d* \

import org.springframework.security.authentication.AuthenticationManager; $ j, ]) r; S3 K& G; { import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; S1 C* k, y0 A0 E: h

+ f2 F+ [5 b; L( y4 p" C0 X% K

import org.springframework.security.config.annotation.web.builders.HttpSecurity;/ u: _% M) U# A( I import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;/ W- M! \) W) m! Z0 p. m1 a/ `7 |

8 Y& a# M; s2 i8 f

import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; R+ c( S( r$ _% t

4 a0 }! o& ^, }2 T4 B- q# u; O

import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;1 g, a) f c/ w2 D import org.springframework.security.crypto.password.PasswordEncoder; 2 v _+ Q, ?* d( r0 J2 O0 L2 T' I8 [2 A# \

B! q0 a+ G j! [: n* j. x6 [

/** 2 g$ m; H& }7 q! k% }/ j * @Author ChengJianSheng. C4 x& o2 k# z9 q3 ^ * @Date 2022/2/145 O& k# Z+ V( c& W9 |+ }: ]3 @ */@Configuration@EnableWebSecuritypublicclassWebSecurityConfig

# C5 S5 j2 s# {

extendsWebSecurityConfigurerAdapter{ ) u0 Q7 c! N6 Q% @% K3 {/ `; q0 M& u! v/ I/ b6 T, g) B8 n @Autowiredprivate UserDetailsServiceImpl userDetailsService; ; X% b6 A+ q" X4 k; @, J ; L( I* P- p6 Y3 g! ^& |% ^# s$ i

: L# D& h. j8 j) Q, m% F2 h9 m

@Overrideprotectedvoidconfigure(HttpSecurity http)throws Exception { 7 E, D' R6 x8 f8 a http.authorizeRequests() + @% i( l# ]- a+ b( W- Z0 R .requestMatchers(EndpointRequest.toAnyEndpoint()).permitAll() ( ?. d0 n. ~( {! A+ o; S/ v# ] .antMatchers(

8 F; O5 H' O, o

"/rsa/publicKey", "/menu/tree").permitAll() 9 L: ^/ ?/ J; h .anyRequest().authenticated()' \& `: D/ }, X( e g0 u .and().formLogin().permitAll() 3 Q+ ?% p6 `3 x$ U7 ^: U! _+ O# P .and()( u# i9 r( @9 _- H n3 a .csrf().disable();, T a \) e' v0 {! f1 ]* D0 D }" d) C* M9 a2 a% L N2 h) a6 V* u7 A1 Z e9 V- W! l

6 U D' G! |" S* P* i# o% C! c

@Overrideprotectedvoidconfigure(AuthenticationManagerBuilder auth)throws Exception {% y! i! |; m9 t+ A" b$ |$ g auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());( Y4 z* ?; {2 n' `$ z# z+ f }& v* F/ P9 w# w/ I2 \& H- o& b4 v * x8 {8 y* m) t4 i6 `4 h2 c

# o6 K Q% P% t/ i* o" }

@Bean@Overridepublic AuthenticationManager authenticationManagerBean()throws Exception {4 I# Z- B$ U9 c0 V return

( ]$ F* @, b- k- N! j& f7 M

super.authenticationManagerBean(); v0 ?+ _7 \5 D/ Z$ H& c( d1 J } ' S! q2 t4 G1 s# u7 U4 B3 v9 r9 k+ e" E, ? @Beanpublic PasswordEncoder passwordEncoder(){ ' f( W2 x) {- ^3 ?9 t+ ?

5 x; N$ B" g: n ?2 m

returnnew BCryptPasswordEncoder(); 1 s6 Z" y% h& f m }) F! U3 M' S$ J) _1 ^! B3 V, v }UserDetailsService实现类package com.soa.supervision.uaa.service.impl;) z1 h: u9 J$ T0 W! D$ m 9 A) u( \& S8 J; ?

7 P% Q' V/ W9 \' ?: M2 @! n& W

import com.soa.supervision.uaa.domain.AuthUserDTO; ' n' B" e# N6 } A0 L" N) f7 F import com.soa.supervision.uaa.domain.SecurityUser;1 P# D' j: {4 t) N" L4 }- D9 |3 [

: @0 O+ t4 X5 R, p

import com.soa.supervision.uaa.service.SysUserService; 6 H, g ?: h+ [ import org.springframework.beans.factory.annotation.Autowired; # F8 u' e, ~- Q3 B5 I- t( s

{# x/ k* y1 x, T

import org.springframework.security.authentication.LockedException; & T: M! }! T( ^! Y& A& E% q; ^. k import org.springframework.security.core.authority.SimpleGrantedAuthority;# r! P O9 q8 k# A

! A0 ^9 \& S3 h1 X2 C

import org.springframework.security.core.userdetails.UserDetails;) O# ?8 [, L Z! A& h+ L import org.springframework.security.core.userdetails.UserDetailsService; 9 w6 |1 x0 K8 F

7 n U/ R: V2 ?% X3 o

import org.springframework.security.core.userdetails.UsernameNotFoundException;& b: h5 R/ a1 p2 a2 O& b2 q import org.springframework.stereotype.Service; N0 p' ~: w2 P ' n3 ~2 @# g J" U0 M4 ~% s

( V8 W8 W4 i5 m+ W3 \6 Q7 z

import java.util.Set; , x( h4 L1 Y* ^ import java.util.stream.Collectors;0 y& p6 N1 V! F6 x : U2 w- m- x- t+ B% {0 N' O' i4 u9 |3 K /** ; k: y7 t9 V, T! ]7 | * @Author ChengJianSheng0 e1 [( A2 d0 X4 ]! O4 t7 T7 \ * @Date 2022/2/148 \. H c3 |' P* m# K/ f7 \! I */

. [6 d" r3 I1 r0 R: q. W6 X' b

@ServicepublicclassUserDetailsServiceImplimplementsUserDetailsService{ 2 o& u0 i9 m/ O9 R6 f' f& L' [6 Q, z4 `# {; ~ L3 a% `" o @Autowiredprivate SysUserService sysUserService; 5 P* G$ z5 E9 c3 l3 G' a7 G3 X/ B7 }) b8 j5 e; q

0 I) S: Y7 O2 F# y" @

@Overridepublic UserDetails loadUserByUsername(String username)throws UsernameNotFoundException {+ Y2 ~( C, ~% h; K6 q AuthUserDTO authUserDTO = sysUserService.getAuthUserByUsername(username); , j; ]# B) `6 l. `

8 Y1 j; _4 x3 @1 `0 ^

if (null == authUserDTO) { 8 k1 O1 ]* y2 `6 I3 Y% M9 u; H thrownew UsernameNotFoundException("用户不存在"); & n* ~$ y9 y5 n } " ~& m' w w" b; ]4 f `% t+ I/ O

6 ^1 | i- E" m( c$ L* {! T

if (!authUserDTO.isEnabled()) {& O7 [( V% K+ i3 f( U7 U* }" {: N thrownew LockedException("账号被禁用");* y+ h4 Q! m: z4 K! Q }% K' m: n' ?; _+ p Set authorities = authUserDTO.getRoles().stream().map(SimpleGrantedAuthority::

1 N/ x- a! c4 r9 Z9 O: c9 o) S. z+ d

new).collect(Collectors.toSet()); 9 D' ?1 Z8 C4 h% t# l returnnew SecurityUser(authUserDTO.getUserId(), authUserDTO.getDeptId(), authUserDTO.getUsername(), authUserDTO.getPassword(), authUserDTO.isEnabled(), authorities); . E) l+ \; X/ t2 J9 ~# ` }6 a% ^. z% [. I# V8 c4 a- B" y" U# n }

6 [* O* l" R+ m9 r6 H$ g

SysUserServicepackage com.soa.supervision.uaa.service; 5 u t( E3 m2 R1 q: c' p9 Y # J% E# C* t+ S" a8 ^ G. I7 @ import com.soa.supervision.uaa.domain.AuthUserDTO; $ Z) k* T% K/ n: T' U+ W* \+ o

/ K2 V7 _9 z4 d% Q4 x

import com.soa.supervision.uaa.entity.SysUser; : X( f6 M* j0 T: z* o) w; C import com.baomidou.mybatisplus.extension.service.IService; 5 l" A0 j* Q* h5 \! ~" H8 P# x2 @( g( v( Z( y; f! w

- ~4 n* x9 P& T$ }

/** # f+ g: M, O2 B *

2 u1 w k8 A) I% E! P) N0 l. @

! w+ v$ J' [) v9 F! D * 用户表 服务类 & j1 t; w; Q0 u- n+ j) o *

9 h0 E4 o8 C4 f) K3 C1 ~ * # M u1 y/ B* @$ q; R) n! q! m * @author ChengJianSheng* N$ U: P9 H4 Y0 f1 b" ^6 t( N6 A5 o5 v6 Y * @since 2022-02-14 / Q0 {4 G3 @# {' M4 ` */publicinterfaceSysUserService

extendsIService { ( R+ a. d/ f9 ^+ L6 x( Q AuthUserDTO getAuthUserByUsername(String username); ( }* i7 B; ^! a. ?4 \/ f8 S& m9 P+ ^ }AuthUserDTOpackage

0 i6 p: L, H E6 ]6 |' Z z

com.soa.supervision.uaa.domain;% U" c$ t W4 c* { ! Y8 E% |. K" M, \( s" w N import lombok.Data; U, `, @' j$ V4 n! a9 c0 Z( r1 K 8 O) t1 X5 `7 ?* T' I3 `1 l import java.io.Serializable; + x( {- r R) c# [! G4 T import java.util.List; 2 Q6 q; \8 t6 V S6 H5 n, \/ k3 M! l* m. V' v

3 W; P% {8 A( @

/*** D) Y D* i/ U2 V) l0 X * @Author ChengJianSheng+ F* e8 i5 B" u& Y * @Date 2022/2/15 1 V x" B R- u( X4 n; k */@DatapublicclassAuthUserDTOimplementsSerializable

! v9 `6 g! z O7 F3 \$ a

{ . {/ l# K" f. J% _* V1 B private Integer userId; % I/ U) V% o) t0 V; ? T private String username;# F& F! E5 n4 I* |: ^* D7 H8 |! d private String password; ) A3 W3 t% C/ v) U0 B" z private Integer deptId;, R3 x1 e+ ~0 P, r' Y: ]5 i

1 k3 C! T9 w7 S I

privateboolean enabled; ( I4 h" v! V ^0 Y: I- v& n! | private List roles; 9 h3 `) o! i0 s4 H6 Z/ v }SysUserServiceImplpackagecom.soa.supervision

' e6 m" @: F5 c4 x( `$ J3 T

.uaa.service.impl;7 F5 @# }& x. q/ C$ j ; H* I+ w1 \. Z% [7 N7 O importcom.soa.supervision.uaa.domain.AuthUserDTO;- {0 I% F5 I1 r: L& a: ~ importcom.soa.supervision.uaa.entity

, ]7 T+ U& ^$ k5 p1 f) \* C

.SysUser; % u* {$ z4 U; M+ n importcom.soa.supervision.uaa.mapper.SysUserMapper;& h$ G% k( N: W2 I$ a! P importcom.soa.supervision.uaa.service.SysUserService

; ?1 R; ]3 b1 b: t3 N' s$ [8 b

; , o( e7 U% t, Y, s; o# W importcom.baomidou.mybatisplus.extension.service.impl.ServiceImpl; * D; M6 o0 c8 v2 _' v5 Z. ]' ~% f importorg.springframework.beans.factory

# _1 n l/ j ~! X! @7 j) J

.annotation.Autowired;- f# i# a% r/ S+ \) a1 l importorg.springframework.stereotype.Service; B2 _0 |# J5 E, O+ q $ F! ^, h$ X' a /**) P, b+ F2 P2 x; ~5 T2 f* W% L *

& W4 q% y. ~7 h

& |" V. B- G4 W2 @4 ~2 ^" x _5 X9 P * 用户表 服务实现类 4 q) X9 e* t# ?' O O5 S7 D0 w *

& v3 y& T: H b* l9 g *( ?1 q8 w2 I4 V" g * @author ChengJianSheng3 Z* m% ]! m2 p1 h# e: Z% k * @since 2022-02-14: i0 T1 `2 }7 R) p6 f+ O */

@Service ( K/ Z3 G; o4 Q- u" p( f public class SysUserServiceImpl extends ServiceImpl implements SysUserService { % M; q" x/ D3 q* G4 N% D: q% O I# K1 c) v4 x! B0 @+ q1 k% L$ M3 h

) y1 w% u# K/ M5 x4 w

@Autowired 8 |* x |5 B8 M' P) b3 r" l private SysUserMapper sysUserMapper;( z! V/ Z/ q+ f/ N* Q5 a* m. Q& M) I 2 Z- D5 g( [6 S2 _- g @Override$ I \ I5 Z: Q7 F" l public AuthUserDTO getAuthUserByUsername(String username) {% m3 L, l, }. N' c

' z; j/ |# t% p5 O( ]/ A: j

returnsysUserMapper.selectAuthUserByUsername(username);: v& U& r* H( U. @0 @# f, U) q4 z* p" J }/ z9 L5 @$ f+ Q- ^7 b }SysUserMapperpackage com.soa.supervision.uaa.mapper; C9 g: O! |/ U8 I. e- W# S1 P: q0 x2 t4 y u

7 Y0 O$ [5 n( a/ @

import com.soa.supervision.uaa.domain.AuthUserDTO;/ l8 D. K8 M+ T( B0 } import com.soa.supervision.uaa.entity.SysUser; % U; A3 J1 {" Q$ R" |9 h8 M4 G4 e import

% M6 s m9 {% Q) h* v

com.baomidou.mybatisplus.core.mapper.BaseMapper;; L9 D7 M f; w" _( X, _ 3 D- |1 d" B6 @! d W4 X2 }7 Z /**2 C& m$ A3 ], G! V. E9 w * 用户表 Mapper 接口 # W5 K* x z" D% u" p * ! @! m% I. ]+ m4 y * @author ChengJianSheng" c$ _8 _% R9 U *

1 o% \- u4 v' G& o

@since 2022-02-148 `6 Z* x' I, k5 H& ` */publicinterfaceSysUserMapperextendsBaseMapper { p3 T+ k( Y6 b) l: f AuthUserDTO selectAuthUserByUsername

. f. ?2 I9 e% _$ b% D' F

(String username);3 d9 J. s9 c0 z5 [ s5 e4 s9 u }SysUserMapper.xml

; Y$ F( Q3 B: ?3 R

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

6 u0 |: _9 R9 ~9 W" s

>

& F2 O8 V- a/ q$ s

column="id"/>

" @7 u' d( i1 m3 Q* }

/>

) ~/ f8 I/ I/ j9 N: e3 {2 L

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

6 j! x5 D3 h! y9 S% J! a0 u

8 o& ~5 f: U+ [% d SELECT ( G# s8 { `# V9 O t1.id,( ?( m: p& X& X4 r( Z t1.username, ; l& |! ~! x& |5 y' r' }, l8 s t1.`password`, : a# [5 v4 g0 H, ^9 E t1.dept_id,2 J" \, f& V1 G' ~9 j% y2 }: K t1.enabled, . t; E# ^7 Y7 o( X/ O3 d8 U t3.`code` AS role_code4 _2 b# d+ B1 c7 L- G8 j FROM3 v3 ~$ [# j& {" i7 y7 m% R sys_user t1 7 j. q1 i* N% z$ j9 P$ [8 E. Z LEFT JOIN sys_user_role t2 ON t1.id = t2.user_id + E+ D/ ]$ w. l LEFT JOIN sys_role t3 ON t2.role_id = t3.id0 A7 n/ j' M; [- K3 r; w WHERE / Y9 X7 L9 q% J0 `4 {. g! @ t1.username = #{username} & Y3 [4 V% J) j4 U1 Z

5 l# {( w1 P: Q' e+ N! P3 x

UserDetailspackage com.soa.supervision.uaa.domain; ; w8 N7 v: Z8 t& B2 \/ M1 }+ N! b ; h; f( k. T( f, {: G import lombok.AllArgsConstructor;0 a' Y' q- R# w# d- q7 K" D" I0 Z

5 D; ^' |7 w& |" B& M

import org.springframework.security.core.GrantedAuthority;" v4 `& U2 n( ~& K/ } import org.springframework.security.core.authority.SimpleGrantedAuthority;: Z: j* y- Q$ p( ]- T! Q

1 ^; [/ |% N+ P

import org.springframework.security.core.userdetails.UserDetails;0 q, O9 d- V2 n 5 C1 M# [9 T' C3 o7 | import java.util.Collection; . f8 v: w3 U/ B( d l, \ import

9 j# y% S% H) t, k8 x8 U* B% t

java.util.Set;3 M! Z7 c" n7 U4 y' P5 H% Z/ G 5 t6 }: p$ d( g6 c' Y! s3 P /** * P+ R; H9 f2 F% ~# @+ r * @Author ChengJianSheng 9 q$ t& Z; m2 B8 Z' a/ M* R * @Date 2022/2/14( q9 C B# x# G6 ]1 p9 g# c */@AllArgsConstructorpublicclassSecurityUser

^3 l4 a4 k! R3 s8 Q* ^

implementsUserDetails{) }8 z9 s; R* S! T /**5 u8 Y- l& w0 w: Q * 扩展字段 ; n4 I& g9 _" M0 v4 d, A0 U */private Integer userId;, z6 q% l3 d+ l0 v private Integer deptId;9 y# G' ? ~; U' w/ C / K# [) M( g+ k: }

' I$ E7 x# u' U$ `+ ]) B. z

private String username; # U0 j' A. [0 U/ G4 t3 z private String password;0 R9 {$ Y7 b# X7 G/ M9 d; O. o privateboolean enabled;& [# p$ |, u$ V; }' K private Set authorities; ) a2 H; }; k! c( w/ @ k % N2 U; q ]# G3 @$ H2 R

. A G6 m' O+ Z; W) x# ^

@Overridepublic Collection getAuthorities() {' U. s8 g! M7 K2 T return authorities;! O0 b# M0 Y H } + H; S. m, A4 ?9 D4 m7 b 7 y/ `0 r. Q7 j5 }. z

5 `$ {8 F1 ^& F' Z3 l

@Overridepublic String getPassword(){$ n% P# |- R' X0 V# V return password;5 }: h2 ?! A! t" m1 [. j { } # k1 p) E1 r" M q2 @3 m& \6 [/ F& {; g3 q: @; H* C @Overridepublic String getUsername

$ U- X: J* q. ^. [6 F/ n

(){ & H$ _. N! J1 `$ x return username;8 k0 v6 U2 P, l0 q5 T. M4 Y0 m }# E4 W9 Z1 |3 L$ z: Y j 9 B T) e/ V% Y1 m @OverridepublicbooleanisAccountNonExpired(){- o8 p* \* W i1 p: t1 u b returntrue

! @+ `" }6 Y# F

;) v' H& y8 i5 O7 i" W } 9 |7 w, V6 o9 F7 q/ e - d' g I1 L4 Y" r! ^" M @OverridepublicbooleanisAccountNonLocked(){1 X1 f2 b) @9 h0 T* g4 s# o+ { returntrue;% ?: o( A3 f$ M } 6 o* b8 W4 I: [ R. o# J S/ f# [: z& d/ p @Overridepublic

: b5 l% }+ p0 y6 {& I6 \

booleanisCredentialsNonExpired(){( J1 X7 |& h+ L6 K0 }5 H9 L" F returntrue;7 `5 ?. @7 v. }5 s! a* [ }' h6 T( j$ W: m3 u/ x9 ]% J 2 F5 V$ I; ^$ Y @OverridepublicbooleanisEnabled(){ 8 O+ a9 C7 B" m! M3 A2 k5 z3 K

! ]% m" @& N |2 ` @3 ]

return enabled;2 b. `- h4 j" R+ m } ( e2 E( v- T3 S( k5 C4 @ L2 ?5 v: ~* o) \. P4 _- |# E. J/ T5 ? public Integer getUserId(){6 a6 m; C2 {3 m8 e" I return userId; 7 U! J* E1 o# _/ |1 D1 G! V- C } ! o9 x5 d9 y/ K" V' y5 F3 i8 k, ]' `( n* w( G* T H; k1 Z public Integer

: H& x" f+ p F9 G7 Y

getDeptId(){/ ^+ h6 `% x1 H; v return deptId; 5 p) [* B1 v. r8 g. f4 } }" k8 `6 S* F* X* ~ }登录默认的登录url是/login,本例中没有自定义登录页面,而是使用默认的登录页面 正常的密码模式下,输入用户名和密码,登录成功以后返回token。

( u6 V( d% e1 g! c

本例中使用密码模式,所以写了个登录接口,而且也是取巧,覆盖了默认的/oauth/token端点package com.soa.supervision.uaa.controller; 8 _0 m7 T2 l6 w. ]5 N- N8 [& \8 N( D* v$ `# m* m import com.tgf.common.domain.RespResult;5 C' I( j! f- h* G: F

n; ~9 o: W" f' I

import com.tgf.common.util.RespUtils; 5 c- {6 {! y! U- o9 z/ f% c import org.springframework.beans.factory.annotation.Autowired; 3 N2 _; C/ J1 ^

9 {# `/ p% H* J/ E& |

import org.springframework.security.oauth2.common.OAuth2AccessToken; 3 ?8 ?9 {4 U! d+ R import org.springframework.security.oauth2.provider.endpoint.TokenEndpoint;* _- x8 h! E. D/ [/ \ |

; ~4 R! S5 |3 I) m( g) K, N- r7 O0 c

import org.springframework.web.HttpRequestMethodNotSupportedException;3 t; B. i' V# x import org.springframework.web.bind.

( C9 M: R5 z: u2 A( n$ A

annotation.*;, o- _) E9 v3 M3 k' S# x - m+ a J2 H4 ~& g: l o* X% D import java.security.Principal;7 x$ B1 E) u/ p1 ?4 q p7 m5 N import java.util.HashMap;, F4 z8 c U( j- n3 s! v' e import java.util.Map;% m) Z1 V* W/ o4 G $ o/ e/ c9 c/ e1 y$ z( \ /**8 U9 P5 o3 [: D2 u *

- j# A! @$ K! B' g4 B' m1 P) Q

@Author ChengJianSheng9 }9 v# ?# {4 f8 }* Y * @Date 2022/2/18 " I, A! D# \. M */@RestController@RequestMapping("/oauth")publicclassAuthorizationController

* m6 d+ u- V1 |2 \7 p: K8 |* D0 m

{7 ^; U+ d8 R+ G 7 _! j. z% S& X: W* x3 f* S @Autowiredprivate TokenEndpoint tokenEndpoint;) w) J3 g! s' _ 4 \/ M+ }: W G9 | /**, i' `' l- @" z. |, u, k" ~ * 密码模式 登录% A! H3 k* M* l3 M * @param principal % S0 u: S N8 W% H, L *

! x1 P" { L F6 P }" h# h

@param parameters * V# l! n2 G( w* u1 ` * @return k. X0 g$ _, v * @throws HttpRequestMethodNotSupportedException* T- g) W1 V" l) T/ ` */@PostMapping(

P8 p. V# i3 n: m

"/token")public RespResult postAccessToken(Principal principal, @RequestParam Map parameters) throws HttpRequestMethodNotSupportedException { ' T' ^* G2 b8 e) R# |' x OAuth2AccessToken oAuth2AccessToken = tokenEndpoint.postAccessToken(principal, parameters).getBody();4 q( {/ [) m" Q9 S8 X Map map = new HashMap<>(); # Y$ L& G3 u1 I" O# X

1 u) H: Z- P7 ?& \# m" Z0 F- \

// 缓存return RespUtils.success(); : d1 J! _# q7 E& N9 d8 a }9 ` n. b; b0 R7 r # u4 @( s5 |7 {* t. R /** . K' v: ?5 A& p! v * 退出" w6 w1 P! d( j* w+ w * @return" X/ U& S( ]+ B */@PostMapping("/logout"

& s9 b2 O- G3 i, P: m8 E

)public RespResult logout() {5 u. f0 n" S- q9 Y* W I/ q 3 d1 u+ H" U; W' v& ~) A: C // JSONObject payload = JwtUtils.getJwtPayload();// String jti = payload.getStr(SecurityConstants.JWT_JTI); // JWT唯一标识

3 |9 G- e- i+ E8 n3 @ ^* t# X( X

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

& o. f, S; {5 u3 ?$ @ X$ O

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

) H* `& ^$ Z5 e g4 \9 p

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

( l" q( c8 |" K! `- T4 f5 {+ y% A

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

5 b' R( }6 g' R9 B4 r- J, ?" {* \

// }// return Result.success("注销成功");return RespUtils.success();. H3 z8 E) r8 F- N: a } 7 ]! K9 u9 I" c1 V. p$ u# c* E }补充:授权码模式获取access_token

1 F# ^* r5 q7 m

菜单登录以后,前端会查询菜单并展示,下面是菜单相关接口 SysMenuControllerpackage com.soa.supervision.uaa.controller; * c/ |3 r$ \! J Z* I+ e# j8 Z' E' Y& {. h& ^8 M6 P2 \ # W* a& ~1 a+ t, X import com.soa.supervision.uaa.domain.MenuVO;0 |/ o `" G& A3 T

2 ~- s5 d8 U! v

import com.soa.supervision.uaa.service.SysMenuService;6 O! ]3 d# O; [0 j/ T) \ import com.tgf.common.domain.RespResult; # I4 v( q# R) Q1 t* } import

. W" u: s+ i9 V0 E

com.tgf.common.util.RespUtils; ) F5 H" ~! X, c import org.springframework.beans.factory.annotation.Autowired; ! h9 k- U$ o% k- _, d import

" [) x1 O" {# R* O& O. b

org.springframework.web.bind.annotation.GetMapping; ) N5 }+ D7 N: y$ ? import org.springframework.web.bind.annotation.RequestMapping;( J5 H& h' ?/ `% j# t; m S; g% z + M* \4 m9 V5 q$ e6 [7 H" ^6 u! e

& M9 a6 A9 K/ f

import org.springframework.web.bind.annotation.RestController; 7 x3 e4 z: c* Z& }, n ) i4 P8 e2 ?6 z$ w% @7 w! H import java.util.Arrays; ; e! t3 M/ G' h" B Q( ]* }" d# ?. L import java.util.List; # |8 G, n$ H, @ 9 L+ w, L, q" D H; R4 O

! B" b. ]/ R9 c9 f. z9 t5 N

/** ; X+ W) Q0 ~8 \0 \7 u& y9 R, y *

, d8 k! _1 g2 v- X% U! ^

2 l. H, J" m8 r- i2 F * 菜单表 前端控制器+ X4 X) k2 b9 D; O O *

4 l, \( {$ j7 u *5 ?* X8 a2 }+ o- v' x4 l% i, Z5 l+ R1 { * @author ChengJianSheng ( }& S$ s3 f/ V3 _ c1 V Z! U * @since 2022-02-217 I) F- L# V V */@RestController@RequestMapping(

"/menu")publicclassSysMenuController{- Y+ ~0 j" o. R2 E 9 t0 N6 E, P- n$ h @Autowiredprivate SysMenuService sysMenuService; . ?1 a* S6 R8 T- ~, F2 ~: _) a/ T1 s7 Q1 X) o) m @GetMapping(

$ g, y& z: V9 V- H- M/ t

"/tree")public RespResult tree(String systemCode) { V, E: D( r, U+ I8 y: G; b List roleIds = Arrays.asList(1,2

' K0 v Q" d" P2 U% U) l2 |

);2 t5 _& h, P* ?& g) X List voList = sysMenuService.getMenuByUserRoles(systemCode, roleIds);# K/ A% \. m+ S3 x: g4 M return

6 @- ]2 B% h* f1 I/ v6 l

RespUtils.success(voList); ! S/ F+ \3 k6 w } v. Q0 A) r9 I# I }SysMenuServicepackage com.soa.supervision.uaa.service;* `/ C4 |, R# s! Q' Y ) C( Z' |/ R1 N; M- ~2 X+ i import com.soa.supervision.uaa.domain.MenuVO; 9 z1 |. o7 r6 Z) M5 x+ \

# J6 x8 F( e& m8 y+ t5 x. i8 r+ V! ^8 v, F

import com.soa.supervision.uaa.entity.SysMenu;$ n; A7 r2 ]/ Z. ~8 E& g \+ V ^ import com.baomidou.mybatisplus.extension.service.IService;$ `2 l9 Y- i- S2 ~) _) d6 `+ Q' B ) L! w& W( x7 ^6 \6 g6 q

& U$ E. |) B" j4 O

import java.util.List; & ~& n% C5 X: p! ]- M % n5 j/ P1 r4 W /** ) V1 G% V& q# G% X *

4 H! o9 M) @9 C2 n7 Z9 i0 {9 p

( {* G1 W. E3 s( S/ n. b * 菜单表 服务类, ?3 ^$ {' j9 N% P *

) O! X& w+ D* D5 I * T1 D+ a7 T! L# z * @author ChengJianSheng: H3 ^# h5 T* X; }9 d, W5 D ? * @since 2022-02-21 - [6 I1 w5 `4 K b. y* e6 U */

publicinterfaceSysMenuServiceextendsIService { 8 O- L# H, m5 Q9 M$ ~8 g) Z List getMenuByUserRoles(String systemCode, List roleIds)

# u4 B8 z& R+ q5 N. c9 E3 _

; 2 R- W! A; z$ _4 h( @; h' f" u }SysMenuServiceImplpackage com.soa.supervision.uaa.service.impl; $ @& z' |9 ?0 x& }5 `5 D& K: P/ O7 \" a/ i- X& b import com.soa.supervision.uaa.domain.MenuVO;" C5 m# G4 Y" Z' U

/ A) N8 ^ \* [* c) D$ D

import com.soa.supervision.uaa.entity.SysMenu; * B0 L- n! Q D import com.soa.supervision.uaa.mapper.SysMenuMapper;+ ~, b) @8 q* w7 a, h% R" u- E( w5 v import

: t6 c* U o# u+ L+ G; h

com.soa.supervision.uaa.service.SysMenuService; - E, G8 J3 {; I; [0 C) S import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; : l; I) o# x6 c' V

& G# X) Q9 o4 }9 Z0 k! a1 V" z/ F

import org.springframework.beans.BeanUtils; , N- o( l% z' V import org.springframework.beans.factory.annotation.Autowired;1 T9 f/ p, N, S0 E% h6 u0 _

2 E. M# ~9 z" w7 [

import org.springframework.stereotype.Service;% \6 E4 f3 e. j9 S- P [ ! L5 j& L& v6 n% u3 L6 W import java.util.ArrayList; ) `2 Q! p- m6 C* w8 z3 j" H import java.util.List;3 c# v$ I& x; N) A import

0 K' r' L5 ~2 E! }. O7 s

java.util.stream.Collectors;8 L- s0 u; o3 h5 K! [4 v ' h: }, R9 B! x! C) n; ]) @ /** 8 i+ I; n( x M( @ *

& H9 F. ~5 N& g$ W# T

' j2 _' t0 }' ]4 F& w& E/ f& [ * 菜单表 服务实现类 8 z( F( v* y( k8 ]3 q *

|% H @; q1 {! H6 ?8 z * 4 q5 m' |+ ^9 O6 [ }" _ * @author ChengJianSheng3 F9 l. q0 L" c3 s * @since

2022-02-21" A' ?; b; y! H' I) V: @9 J */@ServicepublicclassSysMenuServiceImplextendsServiceImpl implements

# {/ N& H/ s& S m3 u$ Z; F! n! n

SysMenuService{7 N% @$ I( O4 g; I9 p9 S8 V " H5 q/ E( |/ F* r6 W @Autowiredprivate SysMenuMapper sysMenuMapper; . L9 `, w8 Q' z5 g2 S. y7 {* o( k# u9 ], N$ ` /**( o5 Z; X7 V* l8 l& C * 构造菜单树 ' K- K5 ^8 v4 A+ g& u * @param

' S" I; z$ {. u8 P! b) z% f

systemCode, I3 n# n& i( k5 C' }, f& @+ | * @param roleIds }" D6 x9 [/ M) f0 f: D * @return- i" o+ B6 g/ s' J. d */@Overridepublic List getMenuByUserRoles(String systemCode, List roleIds) {& s% F: k/ m( \% q/ C p$ P List voList = new ArrayList<>(); / W s2 X2 ^ B# i& K* D + _! \( r. G1 b, r# P List sysMenuList = sysMenuMapper.selectMenuByRole(systemCode, roleIds); # C `* d% E$ w# S

9 _$ z+ f1 V# N

if (null == sysMenuList || sysMenuList.size() == 0) { ( r3 a" w. l- V7 d5 L! h return voList;5 j1 z7 c3 o2 e0 y9 n) Z }) u7 O2 h( V- z List menuVOList = sysMenuList.stream().map(e->{& e" Q7 V8 i$ r' ^. Z MenuVO vo = new MenuVO(); 8 E/ @0 \2 o" K8 H; T9 l BeanUtils.copyProperties(e, vo);3 T8 ~$ M5 h" i$ K3 L vo.setChildren(new ArrayList<>());5 S, b5 d; u1 N$ I) y

8 l; W4 x6 ?5 t8 V4 x! x9 b

return vo;2 h) O& s5 o [- q# Z1 Q6 R }).distinct().collect(Collectors.toList()); ' x( U& x/ e& y+ k - R6 u$ a! G8 ?: y8 |5 m for (int i = 0; i < menuVOList.size(); i++) { : J. K+ _0 c5 E" T0 b

$ \- H+ F6 E9 j4 z% J0 H( ~; {

for (int j = 0; j < menuVOList.size(); j++) {# }) L7 [7 P6 c2 p7 u1 f if (menuVOList.get(i).getId().equals(menuVOList.

; P4 p; s4 B9 L) @5 V* L* b% m

get(j).getId())) {2 J) M+ N) H5 c: G continue; 3 B& M7 i- q/ [ ^4 c. y, x } 7 ?) {4 E7 }3 @8 V if (menuVOList.get

% t, v8 Y& N7 r$ Y5 z+ X

(i).getId().equals(menuVOList.get(j).getParentId())) { 8 \/ }# o: ~4 p3 D' a4 ~4 f% D menuVOList.get(i).getChildren().add(menuVOList.

4 I6 H# h' _3 }+ m, r$ d

get(j));+ K0 h7 W+ K9 f5 t& ]& W4 q } 2 F! R6 L; C/ t! j }$ B6 c" I6 u. P7 Z% G/ r& o4 u } 8 F% ~: b% M; v. l. e* O& F& K6 X: c 8 Z: o/ v$ Y7 z; L return menuVOList.stream().filter(e->0==e.getParentId()).collect(Collectors.toList()); 6 c& [$ p" ]; _+ w: E) G& l } * I) a% u6 w6 Z$ R$ n5 }8 k }

) f2 y/ ^2 ~1 Y C2 {, p

MenuVOpackage com.soa.supervision.uaa.domain; * |8 C! A w% ~3 T! q7 ^" r2 n ; \$ h, X3 A6 \ import lombok.Data;2 d/ j+ O3 p4 T) @& @. f % J: |3 g/ n5 G& U! z import java.io.Serializable; 1 R4 `. Q" `5 p ]# e* A import

0 c7 e6 U6 t' U" L

java.util.List;: l# V/ \6 q8 E" m3 Y) h$ s 7 g { S9 o+ r0 ?$ I /** : ^0 _3 A# p+ m$ P5 @7 L * @Author ChengJianSheng . _. ~; C& n% m; v3 w6 H& T * @Date 2022/2/21 9 t$ m% w! }: u. W6 j */@Datapublicclass MenuVO implements

3 F2 `' z0 J9 Z

Serializable {/ ?, K1 |( |+ A; K( }. ? % a$ _5 ~% S+ C private Integer id;' s0 p9 C8 B% t+ N% n$ e+ N , s9 |4 Q3 m/ s /** ! `/ H7 t$ k' e3 K * 菜单名称" S3 n; G) w( v/ V7 n3 P& r */privateString name; \; g, f% Q a( c& A$ O( j& c3 ]9 m /**% ~; d5 e) r( I * 父级菜单ID8 v$ B8 o0 ~4 P( q E/ Q' M+ C. [ */

: Z/ } B9 X2 D8 k7 m( \$ i3 l: h

private Integer parentId;" N. i3 o( ?- D; Q* H9 L3 y 0 z6 z4 ^# C- w/ M8 ]$ j# Z /**$ b c! K: ?. X& G, A- q) J * 路由地址: g7 _/ o/ x8 z# @ */privateString routePath;9 S- C. v+ n6 z1 \, O $ V: [$ L% V& ?( `5 d8 u6 p+ R' [ /**# |# z( q3 |9 L u* J * 组件# m; i2 |/ I P! L */

# K6 |& g: C/ z' r# N

privateString component;- u" @5 i4 D+ [ ~9 |% ] / T. ?7 g3 ?+ W /** + c7 o. }) N) q* @3 F * 图标 7 z. ]# x) H. }+ k */privateString icon; , x$ F# e: k/ U; d* u( A( U( w; p 2 O Q8 f" j9 V9 ~* D* O) F /**7 j" X& M3 {6 V * 排序号$ B+ K6 g. [3 L */private

: o* g' l, t1 F

Integer sort;" s* d( y; i& o' D5 S. ]7 } " O! {7 ?5 k& P- Q /**# O; I( C! ~! n' Y4 D * 子菜单 + j3 u9 Z: K3 i; Y z0 A# M */private List children;! Z4 p; V; V9 u0 x& a# A }SysMenuMapperpackage com.soa.supervision.uaa.mapper;- o0 t9 ]# K4 _* t4 M7 N , |7 r* h3 E5 E

7 L" x0 d! ~ J6 n

import com.soa.supervision.uaa.entity.SysMenu;8 C+ `+ t3 X9 k5 w% a6 d+ e import com.baomidou.mybatisplus.core.mapper.BaseMapper; t: B2 }% N% e3 m

1 ~, w9 g, L( }& ^# N7 l

import org.apache.ibatis.annotations.Param; 8 k4 y; g: V! \( q0 b* Q3 K0 a: r# }. Z h. {% S3 f' M; _4 |$ y import java.util.List; ' s! ~0 O* m: o) c ' Z& d! ?' [+ N+ ^ /**0 L4 ]! Y( K, J) s4 K *

7 f. [) _( C4 s1 m# j/ r6 S

d8 m; k7 {, F$ ^; d6 }$ m * 菜单表 Mapper 接口 " c, q- y$ u, E9 |: o/ C+ [3 y1 A6 k *

8 ? Z9 T) {8 g7 @. f * / d2 Q, V0 ?" }! d *

@author ChengJianSheng0 l: o# z4 w/ }' h * @since 2022-02-21 0 I, Y- p1 W! s+ f3 \+ a */publicinterfaceSysMenuMapperextendsBaseMapper

- O/ r) Z6 H8 E' Z% [

{9 z5 d4 T- {* t. i6 t; G; U$ \6 c0 P List selectMenuByRole(@Param("systemCode") String systemCode, @Param("roleIds") List roleIds); 6 Z1 r' o5 Z5 X0 x+ | }

3 m/ N. ]( x1 C: V- K3 A- i/ ~3 x

SysMenuMapper.xml

3 l+ u3 `2 w4 C

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

1 V4 C6 C' h1 H- J# Q) T

> ; b8 f; H; o; V; T3 R3 W3 \ SELECT; g4 J" y! {2 `0 o$ ~* s' R" ^: m1 [ t1.* 9 C1 K" s! j- F. i( z# w% p( g, m FROM 3 |- l9 ]9 Q* [5 t sys_menu t1 0 Q4 Z8 M8 E) a3 a# S6 A* w" a LEFT JOIN sys_role_menu t2 ON t1.id = t2.menu_id) _! W/ D2 `. U WHERE ; `3 O2 s2 M ~/ e. ~. ? t1.system_code = #{systemCode} * x6 T6 x: M) W% r2 T/ y AND t1.hidden = 0 w; j F' n. X AND t2.role_id IN

: O E+ @( a! I3 P4 {

#{roleId}7 ^9 o/ E5 u0 d" x9 [ ORDER BY ; c0 g9 M& \9 f5 p, F t1.sort ASC1 i6 e0 f* a0 A$ a+ G1 R

; z" D2 G" ~4 U. V2 C2 m8 `

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

9 h- o& T3 }. o4 g/ E6 B

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

' `: Y! o+ ^! v1 j7 W2 Z

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

+ z8 r: p W: W4 ?5 @

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

I5 }0 s. X/ r/ Z2 u9 S

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

' \: M. r' E6 i$ w- P6 ^" v

首先是依赖

+ j4 \4 X' V! ~

="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"

; K7 R- Z1 B7 }" z* S

>4.0.0com.tgftgf-service-parent9 i4 Q/ b3 T, m! ]/ a+ |3 y0 Y0 Q4 E: N 2 P* z7 j3 O" U+ j( Y" F) o% b

$ Y6 d$ y* n( V/ X7 [) H . S a% V6 J, L$ Y y1 f! r, D7 t5 {$ {4 x

暂时无法加载帖子列表

回复

使用道具 举报

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

使用道具 举报

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

    本版积分规则

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

    GMT+8, 2026-11-1 04:16 , Processed in 0.083319 second(s), 51 queries , Gzip On.

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

    Powered by Discuz! X3.5

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