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