|
, 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:level rg:springframework:security:debugmybatis-plus:configuration:log-impl rg.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 |