找回密码
 加入怎通
查看: 170|回复: 0

Spring Boot Security 整合 OAuth2 设计安全API接口服务(spring boot安全机制)

[复制链接]
我来看看 发表于 2023-03-07 02:44:32 | 显示全部楼层 |阅读模式
7 C3 u' y# o, f

简介OAuth是一个关于授权(authorization)的开放网络标准,在全世界得到广泛应用,目前的版本是2.0版本文重点讲解Spring Boot项目对OAuth2进行的实现,如果你对OAuth2不是很了解,你可以先理解 OAuth 2.0 - 阮一峰,这是一篇对于oauth2很好的科普文章。

6 j. l1 d/ j: W

OAuth2概述oauth2根据使用场景不同,分成了4种模式授权码模式(authorization code)简化模式(implicit)密码模式(resource owner password credentials)

3 V$ F. |& o Q8 u/ l! [5 H. x

客户端模式(client credentials)在项目中我们通常使用授权码模式,也是四种模式中最复杂的,通常网站中经常出现的微博,qq第三方登录,都会采用这个形式Oauth2授权主要由两部分组成:Authorization server:认证服务

`2 I9 Q& ^6 e) i, P

Resource server:资源服务在实际项目中以上两个服务可以在一个服务器上,也可以分开部署下面结合spring boot来说明如何使用快速上手之前的文章已经对 Spring Security 进行了讲解,这一节对涉及到 Spring Security 的配置不详细讲解。

( M9 z9 ]! Y5 ^/ O! t2 r! S

若不了解 Spring Security 先移步到 Spring Boot Security 详解建表客户端信息可以存储在内存、redis和数据库在实际项目中通常使用redis和数据库存储本文采用数据库。

7 X1 c# Y3 a% E/ n

Spring 0Auth2 己经设计好了数据库的表,且不可变表及字段说明参照:Oauth2数据库表说明 创建0Auth2数据库的脚本如下ROPTABLEIFEXISTS`clientdetails`

3 G _* Z' `4 G7 K

; 5 d1 U# ^! }% m. Z! x* r: _$ B DROPTABLEIFEXISTS`oauth_access_token`; H- y/ l9 Y0 W! x! s# x& p DROPTABLEIFEXISTS`oauth_approvals`;" m I1 `/ A; ^+ c% H! k DROPTABLEIFEXISTS`oauth_client_details`

$ W" F6 w% x$ g6 ?/ q6 c

;6 \3 d2 M Z O" L DROPTABLEIFEXISTS`oauth_client_token`;4 k( I8 v' P( R9 l9 h: m DROPTABLEIFEXISTS`oauth_refresh_token`;9 h- T, r) \# k; y2 d# x1 v) _/ E0 k 6 P6 b8 y8 K1 F1 Q% x3 l5 s5 t! A CREATETABLE`clientdetails`

$ A, e( |3 x& b

( 7 L' |1 D* P- R- i& X# N `appId`varchar(128) NOTNULL, ( i8 H/ C0 f8 V9 p4 ?- Z" M9 O( ]( @, c `resourceIds`varchar(256) DEFAULTNULL, 0 f1 l- m1 O- P! t `appSecret`varchar(256)

- U$ A$ Z; u0 A

DEFAULTNULL,' m/ Y; x& N+ t4 L `scope`varchar(256) DEFAULTNULL,) P) L& @: c' `6 y$ y" N `grantTypes`varchar(256) DEFAULTNULL,: L* @5 N l p( x6 h6 V5 ]5 Y8 ^8 l* g `redirectUrl`

/ a' t/ q/ Q+ s( H/ _& d) V5 z/ d

varchar(256) DEFAULTNULL, ! g, h" `6 X* b `authorities`varchar(256) DEFAULTNULL,; W. s+ i" R- W- F' A8 D `access_token_validity`int(11) DEFAULT

/ {- ^5 k. C6 Q4 H. C

NULL, 3 d1 V$ U/ O9 K `refresh_token_validity`int(11) DEFAULTNULL, ; q3 F% {( ?# `, [9 t9 U* D `additionalInformation`varchar(4096) DEFAULTNULL

7 O' \' B. i/ i9 k+ ?3 A* @

, ' L7 n, h# T! z. L6 O, W+ h `autoApproveScopes`varchar(256) DEFAULTNULL,1 g$ Y2 J3 U5 ~2 i' z" } t' o PRIMARY KEY (`appId`) & f5 I% ]1 P$ N# C ) ENGINE=InnoDBDEFAULTCHARSET

9 ~: b$ b% w3 V/ d2 K

=utf8;! c0 D0 w3 ~* K4 J# S. R/ J. H # F% C Q' k4 n6 j ( W. p( j9 i% H# S) M( N CREATETABLE`oauth_access_token` ( 9 x) z3 A' C7 a8 Y; H$ F. I4 N0 W `token_id`varchar(256) DEFAULTNULL,4 t5 t" j3 k5 s `token`blob, + ]: f- T+ \$ @) x8 U `authentication_id`

1 K) o8 E6 N) L7 Z% p

varchar(128) NOTNULL,+ q0 f2 j' ?# R: U `user_name`varchar(256) DEFAULTNULL,- L) D& V' k2 X( O0 e# K `client_id`varchar(256) DEFAULTNULL, . Z5 B! A* t: I

; d2 G" X3 ]# j7 D" b

`authentication`blob,. H0 P( K$ d; \ `refresh_token`varchar(256) DEFAULTNULL, ; N6 g) \* l+ t PRIMARY KEY (`authentication_id`) ; o) k* Z# @4 D) k, g$ @* ?+ T: b )

6 t8 q( t! N. j

ENGINE=InnoDBDEFAULTCHARSET=utf8;6 f7 P$ v# l& k% x% O $ r& b* V1 A: F; P4 u6 y( g: t4 b CREATETABLE`oauth_approvals` (5 {2 r" r/ x6 }2 j- O9 ^ `userId`varchar(256) DEFAULTNULL, # c! o$ e# {& _4 `, K) e K0 ?" H

6 h- G2 G2 n5 Z/ B( p. r

`clientId`varchar(256) DEFAULTNULL, h3 g9 z. J: [ `scope`varchar(256) DEFAULTNULL, 7 l& Y# ?7 z. C$ z# f$ h `status`varchar(10) DEFAULTNULL

, j. l0 Y0 W/ b) M

,' o0 f4 G, }( w" b `expiresAt` datetime DEFAULTNULL,5 ^3 @) s& t) B* l5 _0 V" d `lastModifiedAt` datetime DEFAULTNULL : Z5 o2 O; n/ N7 E+ D% @+ j t* x ) ENGINE=InnoDBDEFAULTCHARSET

+ ?' f2 {# s, Y% M. `3 m Y

=utf8; ( n; }6 j! j8 D" h" S |) }& Y: R$ s CREATETABLE`oauth_client_details` ( ) ]" K4 k$ `* N3 G `client_id`varchar(128) NOTNULL, & R* L# Y9 I% p# V9 q: | `resource_ids`varchar

/ W" k+ o9 k4 ^1 c! g8 u

(256) DEFAULTNULL,3 A. S5 i, _) r. l `client_secret`varchar(256) DEFAULTNULL,$ U: ~+ U) ?- T# s H `scope`varchar(256) DEFAULTNULL, ( F' ]4 h! D. ?' P `authorized_grant_types`

6 B" ~) V* X* \) f$ B0 _

varchar(256) DEFAULTNULL, 5 J" e8 d* |& s. h4 |5 E2 U `web_server_redirect_uri`varchar(256) DEFAULTNULL,9 C3 d% N+ k. S. K$ b1 ^ `authorities`varchar

% f* P5 x7 v5 Y: N7 Y

(256) DEFAULTNULL,: S( {/ M3 r* f `access_token_validity`int(11) DEFAULTNULL,9 ]* i2 i$ B7 r+ q `refresh_token_validity`int(11) DEFAULT

6 k# B" r% k* q

NULL, f8 a- b7 {3 P- B7 \2 Q `additional_information`varchar(4096) DEFAULTNULL, + I' i) _0 B7 b% ]; I2 q8 p) c0 T$ s `autoapprove`varchar(256) DEFAULTNULL,3 E! D s+ M: m5 I PRIMARY

3 T, _( F# R9 b" O

KEY (`client_id`) - X) F3 J. |1 I3 Z9 B ) ENGINE=InnoDBDEFAULTCHARSET=utf8; ; d! q) Z8 S+ O. ]. Q $ t! M0 C8 K) k CREATETABLE`oauth_client_token` (6 J9 m8 A- N; s `token_id`

4 u, C3 [" h# ]( n- I: R

varchar(256) DEFAULTNULL, ; f- m- ^. A! O `token`blob, 3 O7 a% r+ j8 ^7 f. B: t c `authentication_id`varchar(128) NOTNULL, $ ^( v+ _0 B2 ]; f( C `user_name`varchar

' \# q. H9 r7 }' K

(256) DEFAULTNULL, 7 a9 T9 ~3 ]; T `client_id`varchar(256) DEFAULTNULL, 1 m. {9 m. m( O& B$ T PRIMARY KEY (`authentication_id`) / c3 [4 [3 \$ L: | ) ENGINE

+ o4 N) Y% f# O- L( R) ~* Q

=InnoDBDEFAULTCHARSET=utf8; 8 r/ v( U' B& V* g% }' X& Q! Y! J$ k+ ?! L m, [ DROPTABLEIFEXISTS`oauth_code`; ; g4 ^! F8 y, n* h1 E CREATETABLE`oauth_code` (& x2 P/ M9 O4 i3 I0 @$ G `code`varchar

9 t5 f7 W6 f; J1 c

(256) DEFAULTNULL,& c v* Z Y6 {" Y `authentication`blob 5 ^- X- q8 n/ X% q- U ) ENGINE=InnoDBDEFAULTCHARSET=utf8; $ D3 o* a: w" l! Z / d; |1 U. G' a' |2 E( B; O; A CREATETABLE`oauth_refresh_token`

( K, b+ o6 P L/ w, W' A! g( ? v7 R& {

( {# H' s( P! `: i/ L. | `token_id`varchar(256) DEFAULTNULL, ( V; ~- U* E4 u; ?& m. m `token`blob, V& X) P: \; v% V `authentication`blob # F/ K0 i6 u, t2 X; G# C2 _+ i4 d ) ENGINE=InnoDBDEFAULT

: Y5 U8 X1 e% H2 r3 ~

CHARSET=utf8; " g8 P/ T% c' `* S5 ^' E 为了测试方便,我们先插入一条客户端信息INSERTINTO`oauth_client_details`VALUES (dev, , dev, app, password,client_credentials,authorization_code,refresh_token。

8 z# N% r9 j9 @/ @9 S% Q+ U

, http://www.baidu.com, , 3600, 3600, {\"country\":\"CN\",\"country_code\":\"086\"}, false);用户、权限、角色用到的表如下:

) _, m2 I; C+ [6 r+ E

DROPTABLEIFEXISTS`user`;' h# E$ y6 G @: N& u DROPTABLEIFEXISTS`role`; # ?8 \+ R E' F! A& p DROPTABLEIFEXISTS`user_role`; 1 k! L( @& h0 Q4 Z DROPTABLEIFEXISTS`role_permission`

) p; |* ^% Z' l; f1 C; x+ r/ n$ s

; + u$ R* f u9 G3 U DROPTABLEIFEXISTS`permission`; - Q7 ]3 Y- R* x- A4 f , l/ c ]3 `0 l: _7 u3 ~' q CREATETABLE`user` ( 9 I% A/ b0 b4 Z% C, ~+ Y) l# } `id`bigint(11) NOTNULL AUTO_INCREMENT,: {/ x8 M) X* m' N& { `username`

! \5 q0 l, z3 W$ |& r2 B6 n

varchar(255) NOTNULL,1 Q- n4 e' e4 f: V, w, \) P7 Y( F# L `password`varchar(255) NOTNULL, 0 ] D, m7 R" v$ k0 {5 ^/ Z: o PRIMARY KEY (`id`) - M% ^" H5 T# [% w );% G ^$ u3 N. E$ Y8 z0 D CREATETABLE`role` (& P9 p( u1 s' h9 z `id`

4 x) e* a2 X x _

bigint(11) NOTNULL AUTO_INCREMENT, 0 t9 i V G" G+ L) H1 D `name`varchar(255) NOTNULL, s/ y- R# m) b5 i PRIMARY KEY (`id`)4 g# l$ V# }% m2 t/ U# A M# n ); 3 Y1 A' C% u4 X0 j CREATETABLE`user_role`

4 D" |; P; j7 t; ?4 R2 N

( 6 A) L! s _. G3 m' m; T' ` `user_id`bigint(11) NOTNULL, 1 ?' x) h- X9 q, _4 I `role_id`bigint(11) NOTNULL , U% v# n( e) O5 k );! _1 n/ \. [- [) o( P) E CREATETABLE`role_permission` (2 R8 n. S) t. t `role_id`

; h( ^% g* o& }. u6 f. t1 q3 Z

bigint(11) NOTNULL,! X1 m0 h }2 G' K7 Q `permission_id`bigint(11) NOTNULL 1 b: A% O' n7 t; C& ? );1 U5 n H4 o3 p2 n6 ]4 W, t CREATETABLE`permission` ( : @) }- b" T7 J1 Y4 ?* v- L4 e! g `id`bigint(11) NOT

% y# ^$ d1 i# x$ v# l

NULL AUTO_INCREMENT, : l$ ?# ~% ]. e% K `url`varchar(255) NOTNULL, : C9 B0 {& d; o6 X% e7 Z `name`varchar(255) NOTNULL, ; i$ ?, _; C* h1 `$ P `description`varchar(255)

# a2 Y/ U! ]6 [2 Q* k ?

NULL, 7 m. p* V' X: l# W" G" e1 A2 B `pid`bigint(11) NOTNULL, 6 g3 y% T: u( N3 ]8 c s PRIMARY KEY (`id`)! Q# X% H2 H6 r b' c ); & l, j, q ?. H5 q5 P, B + J& Z! Z9 q) I# A0 @8 v" B INSERTINTOuser (id, username, password) VALUES

0 a% {: j2 G' J# B: O0 T# B" _

(1,user,e10adc3949ba59abbe56e057f20f883e); 8 Y" I$ q9 \. t9 L9 u4 C8 K INSERTINTOuser (id, username , password) VALUES (2,admin

# I$ L6 I! E3 }% l# C& Y& z

,e10adc3949ba59abbe56e057f20f883e); 8 G6 z C# j/ ^5 z7 N INSERTINTOrole (id, name) VALUES (1,USER);3 O* s9 W8 K9 X4 R7 l8 x7 j$ |/ t INSERTINTOrole (id, name

0 @4 ]" g5 m8 N

) VALUES (2,ADMIN);# X/ {- b& p8 M5 ^: ]) i6 L INSERTINTO permission (id, url, name, pid) VALUES (1,/**,,0); & l4 Z- O6 F L INSERTINTO permission (

4 o0 ?# D( c0 `5 _, D; y: j/ N

id, url, name, pid) VALUES (2,/**,,0);* {5 l7 v6 N4 J$ O' v0 G+ k$ ^ INSERTINTO user_role (user_id, role_id) VALUES (1, 1);) N+ A% E* ~; r INSERTINTO

7 B' D- g ?2 [/ j H

user_role (user_id, role_id) VALUES (2, 2);& l: I5 i# e3 r! F7 U }' k INSERTINTO role_permission (role_id, permission_id) VALUES

- L- \. l# q$ z, j, n% U

(1, 1);2 d& \( y- y) i1 Q" B9 q \ INSERTINTO role_permission (role_id, permission_id) VALUES (2, 2);项目结构resources 6 I- v) m% q8 }* ]4 r |____templates3 e: r0 I2 V' j" S" b# \; P" s |

! q4 A5 _& @% B- |8 G* s; Z

|____login.html # y N. d6 O4 T1 P2 `' R ||____application.yml" N% S& d) ]! g S, v! e& T K; r java 9 d/ O1 R3 O% h& L |____com 3 l- D; e G( s0 j7 n, n | |____gf 0 |! @) W q5 X3 e: W: z2 C) _ | ||____SpringbootSecurityApplication.java ; ~- r5 ^1 f0 A* o l |

: |# U0 ~6 r% D% q% M

| |____config( b% U* |! P! V8 Y% l( n | || |____SecurityConfig.java$ S6 b7 Z* I; `7 ?) G! x+ }" i$ q8 h | || |____MyFilterSecurityInterceptor.java 2 W4 H ]* ]. U7 [0 n- ~- F | || |____MyInvocationSecurityMetadataSourceService.java ! A) [/ b7 o" [4 @6 } t

5 y/ r( m4 K- D

| || |____ResourceServerConfig.java ) @& W! T# K: ~& R$ d* S | || |____WebResponseExceptionTranslateConfig.java6 ~% v. K1 H- |3 R- c | || |____AuthorizationServerConfiguration.java' o4 t- q& N6 i

! G$ R& o0 t, f4 x$ t( x

| || |____MyAccessDecisionManager.java1 y4 a& o% ]# U+ ~ | ||____entity : ]) X, Z# M- Y6 F% e* V3 S || ||____User.java 2 R" r$ t7 S* Q# n || ||____RolePermisson.java 5 G* B0 f9 f n# x2 @ |

9 }4 H5 B' r5 }

| ||____Role.java # z" |/ } ]0 O: m) ~* A6 W6 U; A || |____mapper 5 ]' W% C8 q. ~1 }- k: b7 _' Q | || |____PermissionMapper.java , C' K8 q; b% \. E | || |____UserMapper.java ) y' U- O- Z5 r& c _8 U | || |____RoleMapper.java ! v2 d/ {1 \- T0 ]( G% G

" d1 f) }4 z) m7 f9 E! `

| ||____controller* u4 k$ e( f8 N+ R! _, d) y || ||____HelloController.java : [- d$ {" x! a" G& |' O" p8 C) R || ||____MainController.java 1 l4 Y+ n1 H# \5 @) I- b || |____service z. o6 k) D) O, @1 Y" A2 l& l4 X | || |____MyUserDetailsService.java

/ ]0 Z2 g! L" e. l: A8 f( }

关键代码pom.xmlorg.springframework.bootspring-boot-starter-security

* N" x N2 O7 r* q& X

org.springframework.bootspring-boot-starter-thymeleaf

3 a+ H- N, P" q3 ]% y; I

org.springframework.bootspring-boot-starter-oauth2-client

% p+ M7 o: m' O) e Z9 Y! T7 ]

org.springframework.bootspring-boot-starter-oauth2-resource-server

) k& h0 r" n2 t- X

org.springframework.security.oauth.boot

) Q( i0 N9 b0 F( t: ^- _0 B

>spring-security-oauth2-autoconfigure2.1.3.RELEASESecurityConfig

) R. ], Q1 M: A7 h

支持password模式要配置AuthenticationManager@Configuration@EnableWebSecuritypublicclassSecurityConfigextendsWebSecurityConfigurerAdapter

5 c( ]/ g D6 H7 w1 A2 g

{ 0 P7 ]5 K) O; j$ b, @8 v& u4 }1 P' l9 o) L D @Autowiredprivate MyUserDetailsService userService;; G& ^0 N! y" m5 D) h . Q) ]0 B. j. K 0 _' }% P0 e7 Y! v m' J0 `' E @Overrideprotectedvoidconfigure(AuthenticationManagerBuilder auth)

" F$ O0 J O% O& J) R# C' h

throws Exception { $ e1 f5 T/ m$ G' Z; G% _, e6 p( C 2 E* a1 I; n1 c" d, A( s) Q9 s //校验用户 9 k2 {% d% ^/ Q/ u/ I6 | auth.userDetailsService( userService ).passwordEncoder( new

. X. G; O0 j5 b, X$ N- w7 ]

PasswordEncoder() { - b4 H$ C; T! s, {/ N //对密码进行加密@Overridepublic String encode(CharSequence charSequence){ 6 |9 N _, X, O2 o System.out.println(charSequence.toString());; s" t4 z% I+ i2 T+ k

6 I5 i L u8 A4 ^0 g9 b# ^- {

return DigestUtils.md5DigestAsHex(charSequence.toString().getBytes()); - Q2 [% o( E# y/ k6 z; t* R } " R$ ~: Z- ^$ @) M: i/ Q8 q //对密码进行判断匹配

1 V; g* h3 m: O8 v! u$ T

@Overridepublicbooleanmatches(CharSequence charSequence, String s){' p6 z# v; @- p& ?* i% N String encode = DigestUtils.md5DigestAsHex(charSequence.toString().getBytes());' A+ C9 u4 k) P! Q. J

/ A+ ~. z4 R* i) D4 K9 H7 S: G

boolean res = s.equals( encode ); . V3 O( C9 j f return res;- O" U- E# e2 ~% I. v+ [ } $ s0 S d P; i. s } ); 4 Q) R: S! t: s' q& k( Z/ P) L/ Z, ]. m1 X7 m } 0 S: L0 e& f$ Q' g8 k% Q ! F1 r) Y1 M) q' G& h. N0 d

# }* M& \8 Q* o+ v. P1 [

@Overrideprotectedvoidconfigure(HttpSecurity http)throws Exception {9 E/ @& p$ K% E http.csrf().disable(); ' N3 K5 H: D8 t1 `% c1 V( O9 Y http.requestMatchers() / g3 a" H. Z4 D$ Q .antMatchers(

& Z4 p) d# F1 {$ Z' s& o3 L- N

"/oauth/**","/login","/login-error") 2 L: i$ h. q# K# I3 A .and() 5 n$ z3 \: S; j$ ^8 k) O .authorizeRequests()+ m- ^/ u! [. ]* a- U* o .antMatchers(

( i0 j2 u) ^2 f/ m

"/oauth/**").authenticated() 2 J5 M! U- a7 `6 c: v, h4 a .and()5 r+ M+ H8 [1 m1 m; S .formLogin().loginPage( "/login" ).failureUrl(

; F% r" G# y2 G+ h

"/login-error" );" S" s! b u& v- W, q* c5 V } ) F4 s0 x9 O0 v' M! N' x4 W6 j 1 B: [) V' | N2 `% s ) l. z5 C1 s) Q( u @Override@Beanpublic AuthenticationManager authenticationManagerBean()throws

3 b8 B$ ~' Y9 q$ o

Exception{ 4 U8 V1 p0 f4 E1 c0 u( s6 f3 ~ returnsuper.authenticationManager();0 E( l5 @5 U1 E } : D$ ]! ^& \7 `. L5 H' K * Y- i- X" |1 S. j- w1 z @Beanpublic PasswordEncoder passwordEncoder

# g, h/ q0 h1 e9 o7 a

(){7 |8 Z |: v0 z returnnew PasswordEncoder() {8 I, g( R! ~4 P# i) K; ^ @Overridepublic String encode(CharSequence charSequence)

$ {7 c- z9 l0 k! [. Y) i" B3 D+ G: a

{ + a2 x! b- a+ L2 @* ~ return charSequence.toString(); ! e3 T+ U$ Q% Z W& z+ w } / @% a- z/ C/ b% b. P; Z1 D+ L+ _" c& V% M) B0 j3 |$ X @Overridepublicbooleanmatches

* O# b0 G+ J3 y5 |

(CharSequence charSequence, String s){ I) U! I5 @2 c0 Y3 \2 Q3 S return Objects.equals(charSequence.toString(),s); 8 r6 J k# j7 V/ \7 a w2 K9 C1 S } # v- f+ p% d. F };4 Y" s, i# w" [ ? }% E4 e" ^$ ~+ u1 P2 f/ f# c5 Z ' t" N7 y; {- \ S4 ^5 E) ~ 3 ^& z# L0 Z: S/ d$ x }

: V% m9 w5 T6 p" Q

AuthorizationServerConfiguration 认证服务器配置/** 2 M8 N6 j! g0 a1 k7 r8 k5 j * 认证服务器配置) e+ j& T/ n C, g5 s */@Configuration@EnableAuthorizationServerpublic

3 [% T' U7 o9 }7 d3 x; ]

classAuthorizationServerConfigurationextendsAuthorizationServerConfigurerAdapter{3 K. { Y9 K3 v$ K 5 X7 a; ?6 G+ ? ; F |1 j+ X3 |0 }8 F /**" O; W2 D( C! u$ ?9 F( y( w4 v * 注入权限验证控制器 来支持 password grant type ' j- @( t0 `9 N$ ^ */

8 w9 R* P! J6 q9 Y/ s( ]4 L& m! e

@Autowiredprivate AuthenticationManager authenticationManager;9 Y0 F w7 R8 a, t7 G ) k* U( A- O3 _7 x' M5 u3 l /**9 ?/ [* U+ U; z. `6 K * 注入userDetailsService,开启refresh_token需要用到; L2 L9 k/ D# q3 j */

) W- Z5 [( m/ W

@Autowiredprivate MyUserDetailsService userDetailsService; # M7 L6 V+ U; R; V- y' K % U' n$ |. Q: o /**9 Y" Z& Q8 M! ]& k6 N * 数据源: `* w2 ]* l7 x3 U! J# R m */@Autowiredprivate

9 d3 J* H( Z5 J2 ~" Z: T3 Q

DataSource dataSource; 9 P+ f: f% E% `, }1 K( U 3 `& e1 k* @( Z8 Z0 d /** 9 Y9 v3 E* ]9 z * 设置保存token的方式,一共有五种,这里采用数据库的方式5 k6 [. p* A8 M) W1 }3 ^ */@Autowiredprivate TokenStore tokenStore;5 ]$ t+ A6 D6 q* b Y/ w$ _ ? + A, Y% c7 C" M- H& t

: E' _% k( i9 o; `

@Autowiredprivate WebResponseExceptionTranslator webResponseExceptionTranslator; 6 i6 r% [; L6 |8 c7 O3 t8 H" ]# J. W4 F, P% J9 r @Beanpublic TokenStore

+ { A$ X2 u5 |! ^# k4 j

tokenStore(){0 T! j V9 ?8 }& x returnnew JdbcTokenStore( dataSource ); , |8 v" l; c7 K$ s6 j+ s, c% I } 5 f/ K$ f' e) m$ g / \4 ^2 K, w7 t/ o) \1 | @Overridepublicvoidconfigure

8 W+ Z- p5 v- Z$ j9 U+ [; v

(AuthorizationServerSecurityConfigurer security)throws Exception { 4 T) l: A1 B4 {" i( H4 G3 j, u0 E /**5 E* d* s. q! ~( s+ T% a3 E * 配置oauth2服务跨域( h1 i! |/ J6 E9 T% c0 }5 ~ */

. W2 e: J# D. p0 Q* r4 o

2 ?! ?( t7 M$ b4 v s* k% @6 m CorsConfigurationSource source = new CorsConfigurationSource() { * W/ S1 R G$ U# X# Z* h @Overridepublic

9 e& h o ?7 {9 u: K" y( C6 d$ X% b) {

CorsConfiguration getCorsConfiguration(HttpServletRequest request){ + I$ c& z% S9 s/ d CorsConfiguration corsConfiguration =

) @ r J( L* C: i: ]

new CorsConfiguration();4 @# V1 U' r2 X corsConfiguration.addAllowedHeader("*");' n) p- C* {3 W% k& `1 M5 } corsConfiguration.addAllowedOrigin(request.getHeader( HttpHeaders.ORIGIN)); - g# n3 i8 E9 K7 u# Q6 Q corsConfiguration.addAllowedMethod(

5 v# N J' r( d# ?3 h7 M1 W* o y

"*"); 4 ?# U# T; c8 H" v corsConfiguration.setAllowCredentials(true);3 J$ c0 f& Y9 M. V( o corsConfiguration.setMaxAge(

8 [3 P) ~/ w0 K, ^* `4 [

3600L); * a6 g ~/ }& m3 I6 J+ u return corsConfiguration;. ^& K7 Y2 H& G( X5 i }* ~8 v9 T) A4 b. [ };" Y3 c* u+ t! T) H. X! Z8 N . e8 g) y& p6 ?, }" [ security.tokenKeyAccess(

( P A x$ H! g# x& X5 f

"permitAll()")% Z8 f, O$ z: @# R( @1 t$ P- k .checkTokenAccess("permitAll()") : \% `/ E3 Z4 K3 X7 L' `+ v# d .allowFormAuthenticationForClients() 5 g6 f$ C5 F4 f$ S, L .addTokenEndpointAuthenticationFilter(

$ n/ T" M& x+ K5 |

new CorsFilter(source)); 3 p' N, c* T4 j }' U9 g0 D \7 `6 h/ P# L2 z ( h9 V* ^2 A% F9 m: T @Overridepublicvoidconfigure(ClientDetailsServiceConfigurer clients)

3 y. H8 d5 x+ F3 o1 J

throws Exception {4 r( \* Y' z8 h' l f clients.jdbc(dataSource);! m+ K3 E) M1 x; v8 x% z }5 Y% Q9 ]' c& [% \! G g- A " v! ~5 o$ T% b; w1 s @Overridepublicvoidconfigure(AuthorizationServerEndpointsConfigurer endpoints)

# ?: f) B7 V7 z9 q' a- }0 I

throws Exception { 3 w1 f* {" U! ~0 B4 S, K //开启密码授权类型5 N j& V) a2 e8 x endpoints.authenticationManager(authenticationManager); 5 T; G9 \; T/ c7 B( {* E7 @

! s4 N8 T i4 E5 i

//配置token存储方式 5 e- f0 V h8 P9 H: U$ f: z! @ endpoints.tokenStore(tokenStore); 1 }$ z' b, y$ p0 W$ ^& P //自定义登录或者鉴权失败时的返回信息0 Q# {8 p1 R2 F2 c2 K" ~% j: D endpoints.exceptionTranslator(webResponseExceptionTranslator);+ o* i% R4 U2 [( [

# ?9 a( `$ O- \6 c

//要使用refresh_token的话,需要额外配置userDetailsService8 `$ I8 a, [9 K7 o* {8 t endpoints.userDetailsService( userDetailsService );& V$ u: v* c( x5 u$ ]& ~+ h ) ^4 N+ \7 z' J" D& c }; y: r% G- R4 U1 o7 _ 6 H7 j! E9 o( B3 _4 ^1 F ) s, y( ?4 U a5 F }

0 o3 J6 m0 I/ J0 X/ M5 ] D+ I

ResourceServerConfig 资源服务器配置/** 5 r& O/ d- E4 d& L. T0 L * 资源提供端的配置( p# w1 B+ [3 C w# y' T( k/ g7 t */@Configuration@EnableResourceServer- i' j2 z# H$ ~1 K6 `7 f8 j public class ResourceServerConfig extends ResourceServerConfigurerAdapter { ) f: O" c* H7 P% A ' E, D" F2 W4 a0 j L; X

$ l0 x' S* Y+ E+ m. M6 }

/**) I5 V' R7 g7 b$ t: W * 这里设置需要token验证的url5 h" s& D7 Q/ Y4 y9 {# b1 w& U * 这些url可以在WebSecurityConfigurerAdapter中排除掉, 8 b! V! ?0 l( j) S2 K7 k% t( T6 S * 对于相同的url,如果二者都配置了验证% ] [0 _6 T; Q* o * 则优先进入ResourceServerConfigurerAdapter,进行token验证。

2 n! z7 Y) l4 E3 Z! d

而不会进行 ( `9 T( l+ F9 D * WebSecurityConfigurerAdapter 的 basic auth或表单认证4 F: z/ P9 z# r3 B- B */@Override & Y' b6 b: A9 E* _ public void configure(HttpSecurity http) throws Exception { % i$ ?, A6 P2 A: u

: {. Z% P. V- o3 {) [; c3 q! a

http.requestMatchers().antMatchers("/hi") 1 w* F* ]/ u: f0 I .and() ) s; y0 L0 |0 F9 ], n- x! ^( L .authorizeRequests() 4 \ W4 r9 X" { K

" V' a9 x. _' C- l

.antMatchers("/hi").authenticated();1 {/ q' ~: _; [# q# |, ^# X# _ } . M3 S/ y: d( F! d0 a1 a: J) ~' g/ T( T9 [9 | ! i+ X& C) e$ E9 e3 n }关键代码就是这些,其他类代码参照后面提供的源码地址验证密码授权模式[ 密码模式需要参数:username , password , granttype , clientid , client_secret ]。

3 @* v# r( i3 Q; o' j

请求tokencurl -X POST -d "username=admin&password=123456&grant_type=password&client_id=dev&client_secret=dev"

9 ~+ B$ n" Z G! V2 L

http://localhost:8080/oauth/token返回{, w5 y% h! t, V& L* Z% b2 m "access_token": "d94ec0aa-47ee-4578-b4a0-8cf47f0e8639", " G, |+ `5 }, h) ~2 }

/ Q" Q: Q6 w1 W3 G! z

"token_type": "bearer", I) q( i* j( ~/ ^ "refresh_token": "23503bc7-4494-4795-a047-98db75053374", : Z' J/ V& m2 |) I0 G- x" r "expires_in"

% A& q1 e2 r/ J( v1 c$ n, d' b; D

: 3475,8 v' t1 O+ q. U8 g- f "scope": "app"9 a1 i( Z( t6 ]( ? }不携带token访问资源,curl http://localhost:8080/hi\?name\=zhangsan返回提示未授权{% C4 z$ o7 ^2 }5 c2 y3 Y "error"

y% t) F3 j1 i- J) [; m. [ q

: "unauthorized", 1 K1 t, @, s3 P "error_description": "Full authentication is required to access this resource" m% }$ ~0 ^" g' T. k }

0 C) b: f) u9 M- D# I

携带token访问资源curl http://localhost:8080/hi\?name\=zhangsan\&access_token\=164471f7-6fc6-4890-b5d2-eb43bda3328a

6 B; _- q) r4 t. q, _! i

返回正确hi , zhangsan刷新tokencurl -X POST -d grant_type=refresh_token&refresh_token=23503bc7-4494-4795-a047-98db75053374&client_id=dev&client_secret=dev

% S; n6 I# _3 S

http://localhost:8080/oauth/token返回{( G! G, O% }* } "access_token": "ef53eb01-eb9b-46d8-bd58-7a0f9f44e30b", + O) R4 e0 {3 ^! r6 R" [ l+ {

7 E \" j; C8 G' h( t

"token_type": "bearer",+ i# n" l; x* i: D+ B6 l "refresh_token": "23503bc7-4494-4795-a047-98db75053374", - Y" J& H8 H; x6 y6 s2 D& L "expires_in"

a: A( O9 A, R0 [

: 3599, 1 {8 U) ~8 y" \0 c. [ E+ q. U "scope": "app" 1 i, f# T2 q2 T4 g. g$ ^+ y }客户端授权模式[ 客户端模式需要参数:granttype , clientid , client_secret ]请求tokencurl -X POST -d

6 \* f' z$ y0 n& Z4 I

"grant_type=client_credentials&client_id=dev&client_secret=dev" http://localhost:8080/oauth/token返回{3 a9 ]8 U- u& d0 D9 k4 k( Y

5 n8 e5 Y. D% K! T; T

"access_token": "a7be47b3-9dc8-473e-967a-c7267682dc66",* d7 S6 l8 h6 [ s6 O; X "token_type": "bearer", : f) U" V q0 {/ O$ R7 K0 x "expires_in":

" S1 m+ `% V3 }; n1 S" z5 j2 u

3564, 7 L' @ J2 O& \' Q% L6 U1 B "scope": "app"* z" \4 S& u! W8 A7 m }授权码模式获取code浏览器中访问如下地址:http://localhost:8080/oauth/authorize?response_type=code&client_id=dev&redirect_uri=http://www.baidu.com

7 l- J7 e. L9 ]# z5 ~

跳转到登录页面,输入账号和密码进行认证:

* @0 }) q6 b# `( I" D

认证后会跳转到授权确认页面(oauthclientdetails 表中 “autoapprove” 字段设置为true 时,不会出授权确认页面):

' g# [2 g l3 a5 I

确认后,会跳转到百度,并且地址栏中会带上我们想得到的code参数:

3 @3 F, _7 x: G# ?7 j4 a

通过code换tokencurl -X POST -d "grant_type=authorization_code&code=qS03iu&client_id=dev&client_secret=dev&redirect_uri=http://www.baidu.com"

6 s$ D+ L2 l2 A, ?; H6 B" K

http://localhost:8080/oauth/token返回{ + Y; C* c1 K3 C) S "access_token": "90a246fa-a9ee-4117-8401-ca9c869c5be9", ; u5 o; R" }! B1 S0 O+ W# x

- R1 m3 s8 `$ |# b2 \. o

"token_type": "bearer", - ?& i- p7 d: Z "refresh_token": "23503bc7-4494-4795-a047-98db75053374", 9 t$ s% \: o% a1 b1 E "expires_in"

: k4 }0 `1 v5 z: U. r9 R. Q- j

: 3319, ' `& Y# C: G0 D1 Q. e "scope": "app"( ?2 ?" U" v- ]$ M1 p: K; S! G( B- C }参考https://segmentfault.com/a/1190000012260914https://stackoverflow.com/questions/28537181/spring-security-oauth2-which-decides-security

( ^: Y4 Z% J9 r: B/ B( r, l; i

源码https://github.com/gf-huanchupk/SpringBootLearning/tree/master/springboot-security-oauth2

3 f1 ]: I! p! u, q3 _- ] " F0 Y- ~1 t, ^0 A' F. m 4 l# a c$ ?. L ^' X, b$ l8 B0 r' [/ E% C& r& }! K, s 6 k" _# z5 Q+ o9 l" @

暂时无法加载帖子列表

回复

使用道具 举报

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

    本版积分规则

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

    GMT+8, 2026-10-1 20:23 , Processed in 0.040044 second(s), 25 queries , Gzip On.

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

    Powered by Discuz! X3.5

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