纸上得来终觉浅,觉知此事要躬行。
楔子
本文合适: 对Spring Security有一点了解或许跑过简略demo可是对整体运转流程不明白的同学,对SpringSecurity有爱好的也能够当作你们的入门教程,示例代码中也有许多注释。
本文代码: 码云地址 GitHub地址
咱们在做体系的时分,一般做的榜首个模块便是认证与授权模块,由于这是一个m % e K }体系的进N 5 r 3 O W口,也是一个体系最重要最# E W 7 4 C 1根底的一环,在认证与授权服务设计建立好了之后,剩余的模块= y o *才得以安全拜访。
市面上一般做认证授权的结构便是shiro
和Sp0 D C ( a Cring Security
,k 7 x s B x 8也有大部分公司挑选自己研制。出于之前看过许多Spring SecuriT b 2 ~ z , L ct0 O Y G J F }y
的入门教程,但都觉得讲J d T V I B S n i的不是太好,所以我这两天` W x ^ E #在w H 8 6 W . 4自己鼓捣Spring Security
的时分萌生了共享一下的主意,希望能够帮助到有爱好的人。
Spring Securk W h ! Lity
结构咱们首要用它便是解决一个认证授权功用,所以我的文章首要会分为两部分:
-
榜首部分认证(本篇) -
第二部分授权(放在下一篇)
我会为咱们用一个Spring Security + JWT + 缓存的一个dv / Nemo来展现我要讲的东西,毕竟脑子的东西要体现在详细事物上才能够更直观的让咱们去了解去认识。
学习一件新事物的时分,我引荐运用自顶向下的学习办法,这样能够更好的认识新事物,而不是盲人摸象。
注:只涉及到用户认证授权不涉及oauth2之类的第三方授权。
1. SpringSecurity的作业流程
想上手 Spring Sec# _ c { 1 T jurity 一定要先了解它的作业流程,由于它不像东西包一样,拿来即用,有必要要对它有一定的了解,再依据它的用法进行自界说操作。
咱们能够先来看看它的作业流程:] ( O
在Spring Security的
官方文档上有这么一句话:
SprinF , * jg Security’s web infrastructure is based entirely on standard servlett 3 3 ` h j V filters.
Spring Security 的web根底是Filters。
这句话展现了Spring Security
的设计思想:即经过一层层的Filters来对web恳求做处理1 G M 0 U % S –。
放到真实的Spring Security
中,用文字表述的话能够这样说:
一个web恳求会经过一条过滤器链,在经过过滤器y 2 ) J _ } e G E链的过程中会完结认证+ ( X与授权,假如中心发现这条恳求未认证或许未授权,会依据被保护API的权限去抛出反常,然后由反常处理器去处理这些反常。
用图片表k 0 ~ 6 G | ^述的话L ! W &能够这样画,这是我在百度找到的一张图片:
如上t W S J L图,一个恳求想要拜访6 B ( i W [ :到API就会以从左到右的方式经过Y J X w蓝线框框里边的过滤器,其间u 9 f { [ A绿色部分是咱们本篇首要讲的担任认证的过滤器,蓝色部分担任反常处理,橙色部分则是担任授权。
图中的这两M c , $ p I F Y 2个绿色过滤器咱们今日不会去说,由于这是Spring Security对form表单认证和Basic认证内置的两个Filter,而咱们的demo是JWT认证办法所以用不上。
假如你用过Spring Security
就应该知道装备中有两个叫formLogin
和httpBasic
的装备项,在装备中打开了它俩就对应着打开了上面的过滤器。
-
formLogin
对应着你forr # p )m表单认证办法,即UsernamePasswordA[ 8 ~ g U zuthenticationFilter。 -
httpBasic
对应着Basic认证办法,即By Q H t Q jasicAuthentiE R _ ] f Z z 5 /cationFilter。
换言之,你装备了这两种认证办法,过滤器链中才会加入它们,不然它们是不会被加到K E d _过滤器链中去的。
由于Spring Security
自带的过滤器中是没有针对JWT这种认证办法的,所以咱们的demo中会写一个JWT的认证过滤器,然后放在绿色的位置进行认证作业。
2. SpringSecurity的重要概念
知道了Spring SecuritK h V r ! fy的大致作业流程* K k之后,咱们还需求知t f 1 % 9道一些非常重要的概念也能够说是y – r | k 4 r a 5组件:{ x J G ; } X s
-
SecurityContext:上下文目标, Authenj c n Gtication
目标会放在里边。 -
SecurityContextHolder:用于拿到上下文目标的静态东西类。 -
Authenti! q ocation:认证接口,界说了认证目标的数据方式。 -
AuthenticationManager:用于校验 Authentication
,回来一个认证完结后的Authentication
目标。
1.SecurityContext
上下文目标,认证后的数据就放在这儿边,接口界说如下:
public interface SecurityContext extends Serialia f Szabl` R a U y ( Z W /e {
//f x p J } ? 获取Authentication目标
Authentication getAuthentication();
// 放入Authentication目标
void sf ? n = e NetAuthentication(Authentication authentication);
}
这个接口里边只要两个办法,其首要作用便是get or set Authentication
。
2. SecurityContextHolder
public class S- # ZecurityContextHolder {
public static void clearCoL Cntext() {
strategy.clearContext();
}
public static SecurityContext9 a n y s getContext() {
return strategy.getContext();
}
p@ s 4 6 X W kublic static void setContext(SecurityConteV v b B g : # u uxt context) {
strategy.setConteG c L x ( g Lxt(context);
}
}
能够说是SecurityContext
的东西类,用于get ol ^ M – / * Tr set or clear SecurityContext
,默许会把数据都存储到当时线程中。
3. Authentication
public inteU w Lrface Authentication extends Principal, Serializable {
Collection<K r O - V? extends GrantedAuthority> getAuthorities();
Object getCre2 D | W T N c X Mdentials();
Object getDey { [ 2 W M w U {tails();
Object getPrincipal();
boolean isAut I ] d / , h B cthenticated();
void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException;
}
这几个办法作用如下:
-
getAuthorm D ` 4ities
: 获取用f $ g R e r n =户权限,一般情况下获取到的是用户的人物信息。 -
ge! f V U ! K 3 r UtCredentials
: 获取证明用户认证的信3 B W j O息,通常情况下获取到的是暗码等信息。 -
getDetails
: 获取用户的额外信息,(这部分信息能够是咱们的用户表中的信息)。 -
getN O l x A zPrincipal
: 获取用户身份信息,在未认证的情况下获取到的是用户名,在已认证的情况下获取到的是 UserD 6 5 + ^ * NDetails。 -
isAuthenticated
: 获取当时Authe] o Q ontication
是否已认证。 -
setAuthentic - qated
: 设置4 G ! r [当时Authentication
是否已+ 1 O ) 3 A g $ &认证(true or false)。
Authen! % } { T Ktication
只是界说了一种在SpringSecurity进行认证过的数据的数据方式应该是怎么样的,要有权限,要有暗码,要有身份信息,要有额外信息。
4. AuthenticationManager
public interface AuthenticationManager {
// 认证办法
Authentication authenticate(AuthenticationO 1 V q . % K R authenti^ E d n J F f ; Tcation)
throws AuthenticationException;
}
AuthenticationManager
界说了一个认证办法,它将一个未认证的Authentication
传入,回来一个已认证的Authentication
,默许运用的完结类为:ProviderMan` I gager。
接下来; C u |咱们能够构思一下怎么将这四个部分,串联起来,构成Spring Security进行认证的流程:6 U Q I E L T
1. 先是一个恳求带着身份D s 1 ) |信息进来
2. 经过AuthenticationManager
的认证,
3. 再经过SecurityContextHolder
获取SecurityContext
,
4. 最终将认证后的信息放入到SecurityContext
。
3. 代码前的准备作业
真正开始讲诉咱们的认证代码之前,咱们首要需求导入必要的依靠,数据库相关的依靠能够自行挑选什么JDBC结构,我这儿b q Z s $ 5 2 ~ q用的是国人F u O j二次开发的myabtis-plus。
<K O A K jdependency&7 L $ Ugt;
<groupId>org.springframework.f L Pboot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
&k M 0 ? / h % Zlt;/dependency>
<dependency>
<groK 7 2upId>org.sprik [ r + U u angframewor_ 6 zk.boot</groupIdd @ e : ! z>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<depens O 1dency>
<gr= S / ] koupId>org.springframework.boot</groupId>
<artifactId>spring-boF ` n T t m aot-starter-valiW { B H b 8 L m xdation</a% 2 [ A : X }rtifactId>
</dependency>
<u q : h v -;dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</8 1 partifactId>
<version>0.9.0<c x { 6 n/version>
</dependency>
<dependency&gv Z ~ 9t;
<groupId>comj 4 } M O h ] 4 =.baomidou</groupId>
<artifactId>myl a K A $ : W ybatis-plus-boot-starter<} N A D;/artifactId>
<version>3.3.0</version>
</depende0 f 5 p $ncy>
<dependency>
<groupId>mysql</groupId>
<a j m S [ =;artifa} B 4 s gcT = ! 0 5 x z `tId>mysql-connector-java</artifactId>
<versio s q / ` , 4 t #on>5.1.47</versia ( Ton>
</dependency>! U R
接着,咱们需求界说几个4 P t H U 4 I有必要的组件。
由于我用的Spring-Boot是2.X所以有必要要咱们自己[ – y # L m G L界说一( & f M ~ u { m 0个加密器:
1. 界说加密器Bean
@t o F t . vBean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
这个Bean是不必可少的,Spring Security2 n 4 C . Y X
在认证操作时会运用咱们界说的这个} M 6 i R 8加密器,假如没有则会出现反常。
2. 界说Authenticatios { ! 3 L N D GnManager
@Bean
public Authe0 G Y L 2 k G 8 $nticationManV [ uager authent O = `ticationManager() throws EL 1 { v & ^ &xception {
return super.authenticationManager();
}
这儿将Spring Sx 3 x K y W r ]ecurity
自带的authenticationManager
声明成Bean,声明Y t 6 2 $ ( g f它的作用是用它帮咱们进行认证操作,调用这个Bean的authenticate
办法会由Spring Security
主动帮咱们做认证。
3. 完结UserDetailsService
public clas^ y Hs CustomUserDetailsService implements UserDetailsService {
@Autowired
private UserSer3 U D $ S k ( }vice userService;
@AutowireI F ~ m Md
private RoleInfoService roleInfoService;
@Override
public UserDeta1 ^ J s ; b 8ils loadUserByUsername(String s) throws UsernameNotFoundException {
log.! r ^ kdebug("开始登陆验证,用户名为: {}",s);
// 依据用户名验证用户
QueryWrapper<UserInfo> queryWrapperZ K E = new Q; B d p JueryWrapper<>();
queryWrapper.lambda().eq(UserInfo::getLoginAccoun_ 1 W (t,s);
UserInfo userInfo = userSL # E 5 $ G / Nervice.getOne(queryWrapper);
if (userInfo == null) {
throw new Username^ W F G q DNotFoundException("用户名不存在,登陆失败。");
}
// 构建UserDetail目标
UserDetail userDetail = neY ` _ qw UserDetail();
userDetail.setUserInfo(userI* p { dnfo);
List<RoleInfo> roleIni * FfoList = roleInfoService.listRol5 8 = s 8 :eByUserId(userInfo.getUserId());
userDetai2 J 8 k z } E - cl.setRoleInfoList(roleInfoList);
return userDetail;
}
}
完结| ; h B GUserDetailsService
的笼统办法并回来一个UserDetails目标,认证过程中SpringSecurity会调用这个办法拜访数据库进行对用户的搜索,逻辑什么都能够自界说,无论是从数据库中仍是从缓存中,可是咱们需求将咱们查询出来的用户信息和权限信息拼装成一个UserDetails回n L x # W g l N来。
UsS h = qerDetails 也是一个界说了数据方式的接口,用于保存咱们从数据库中查出来的数据,其功用首要r e – s 8 , E [ –是验证账号状况和获取权限,详细完结能够8 P c & S L y查阅我库房的代码。
4. TokenUtil
由于咱们是JWT的认证模式,所以咱! U x 7 L ~们也需求一个帮咱们操作Token的东西类,一般来说它具有以下三个办法就够了:
-
创立token -
验证token -
反解析token中的信息
在下文我的代码里边,JwtProvider充当了Token东西类的人{ 5 ,物,详细完结能够查阅我库房的代码。
4. ✍代码中的详细完结
有了前面的解说之后,咱们应该都知道用Sprint O A N k % $ 6 SgSecurity
做JWT认证+ 3 s需求咱们自己写一X F s i C k )个过l L 6 f 9 _ 4滤器来做JWT的校验,然后将这个过滤器放到绿色部分。
在咱们编写这个过滤器之前,咱们还需求进行一个认证操作,由于咱们要先拜访认证接口拿到token,才能把token放到恳求头上,进行接下来恳求。
假如你不太明白,V ] = 5 g !不要紧,N & 4 @ = G Y g 5先接着往下看我会在这节完毕n E t + R l d再次梳理一下。
1. 认证办法
拜访一个体系,一般最{ .先拜访的是认证办法] 9 , L – } v Y a,这儿我写了最简9 0 _略的认证j 5 ) 0需求的几个过程,由于实际R V U ` , b c体系中咱们还要写登录记载啊,前台暗码解密啊这些操作。
@Override
public Apiv ( d g Result login(String lo[ ; J V ; ? IginAccount, String password) {
// 1 创立UsernamePasswordAuthenticW I N % ? Ra^ ( % 4 H (tionTokeng F R L z
UsernamePasswordAuthen. 7 [ . p /ticaO , otionToken usernameAuthentication = new Use# A d D NrnamePasswordAuthenticationToken+ Y ](loginAccount, password);
// 2 认证
Authentication authentication = this.authenticationManager.authenticate(usernameAuthenticaO | z %tion);
// 3 保存认证信息
SecurityContextHolder.gm G J |etContext().setAuthentication(authentication);
// 4 生成自界说6 ! % t T ] %token
UserDetail userDetail = ` D m R ] M (UserDetail) authentication.getPrincipal();
AccessToken accessToken = jw 6 (tProvider.% u U G Q / , acreat9 ` j a Z z ~ ~eToken((UserDetails) authentica` - $ 4 H ktion.getPrincipal());
// 5 放入缓存
caffeineCache.put(CacheName.USER, userDetail.getUsername(), userDetail);
return ApiResult.ok(accessToken);
}
这儿总共五个过程,大约] / k g E只要前四步是比较陌生的:
-
传入用户名和暗码创立了一个 UsernamePasswordAuthenticationToken
目标,这是咱们前面说+ 9 } | N过的Authentication
的完结类,传入用户名和暗码做结构参数,这个目标便是咱们创立出来的未认证的Authentication
目标。 -
运用咱们) 6 c P从前现已声明过的Bean- authenticationManager
调用它的authenticate
办法进行认证,回来一个认证完结T } 0 n X o –的Authentication
目标。 -
认证完结没有出现反常,就会走到第三步,运用 SecurityContextHolder
获取SecurityContexh # & =t
之后,将认证完结之后的Authentication
目标,放入上下G 1 D ) j G M文目标。 -
从 Authentication
目标中拿到咱/ a j 8们的UserDetails
目标,之前咱们说过,认证后Z ; S @的AuthenZ ; 3 G g & | i Mtication
目标调用它的getPrincipal()
办法就能够拿到咱们从前数据库查询后拼装出来的UserDetails
目标,然后创立token。 -
把 UserDetails
目= . n c c标放入缓存中,便利后边过滤器运用。
这样的话就算完结了,感觉上很简略,由于首要认证操作都会由authenticationManager.ar 6 q x 1uthenticate()
帮咱们完结。
接下来咱们能够看看源码,从k 4 = ? d中窥得Spring Securi! . 6 s % * Pty是怎么帮咱们做这个认证的(省略了一部分):
// AbstractUserD] S wetailsAuthenticationProvider
public AuthL Z q . U I E #enticatic ! _ T o B Aon authenti- f U h C k & S {cate(Authentication authentication){
// 校验未认证的Authentication目H w 1 C标里边有没有用户名
String username = (authentication.getPrincipal() == nJ L c hull) ? "NON* ( 5E_PROVO @ * p t Y ] LIDED"
: authentication.getName();
boolean cacheWasUsed = true;
// 从缓存中去查用户名为XXX的目标
UserDetails user = this.userCache.getUserFromCache(username);
// 假如没有就进入到这个办法
if (user == null) {
cacM e t $ $heWasUsed = false;
try {
// 调用咱们重写UserDetailsService的loadUserByUsername办法
// 拿到咱们自己拼装好的D G J % qUserDetails目n B O 5 P标
user = retrieveUsD [ % ,er(username,
(UsernamePa R & BsswordAuthenticationToken) authentg : N g t o v [ wication);
}
catZ P D W P N B dc= P / = _ o G N PhK / @ v U 5 X y ; (Usernam, % M & w } f M LeNotFoundExc. e W B =eption notFound) {
logger.debug("User '" + username + "' not found");
if (hideUserNotFoundExceptions) {
tf U g a Ehrow new BadCv 9 6 ; L uredentialsException(messages.getMessage(
"AbstractUsere Z H Y { ~ VDetailsAuthenticr l z p v [ F 5 AationProvider.badCreG L Cdentials",
"Bad cru p w w 6 e + N #edentials"));
}
else {
throw notFound;
}
}
Assert.notNull(9 K q V ` [ 5user,
"retrieveUser returned null - a violation of the interface contract");
}
try {
// 校验账号是否禁用
preA@ J ? S 1 1 @ B )uthentG / YicationCX K M z * ? whecky X _ 4 C . z bs.check(F x D l z m Cuser);
// 校验数据; P M = y Y库查出来的暗码,和咱们传入的暗码是否一致
additionalAuthenticationChecks U 4 x T `(user,
(UsernamePasswordAuthenticationTW poken) authentication);
}
}
看了源码之后你会发现和咱们平常写的一样,其首要n * ; A逻辑也是查数据库然后对比暗码。
咱们回来token之后,下次恳求其他API的时分就要在恳求头中带上这个token,都按照JWT的标准来做就 – x能够。
2. JWT过滤器
有了token之后,咱们O 8 f Z要把过滤器放在过滤器链中,用于解析token,由于咱们没有session,所以咱们每次去辨别这是哪个用户的恳求的时分,都是依据恳求中的token来解析出来当时是哪个用户。
所B r f } E以咱们需求一个过滤器去阻拦一切恳$ M B v 2求,前文咱们也说过,这个过滤器咱U 3 ] J 3 F S d }们会放在绿色部分用来替代UsernamePasswordAuthenticationFilter
,所以咱们新建一个JwtAuthenticationTokenFilter
,然后将它注册为Bean,并在编写装备文件的时分需求加上这个:
@Be/ 6 ,an
public JwtAuthenticationTokenFilter jwtAuthenticationTokenFil` / e [ter() {
return neB k 5 Z [ + )w JwtAuthenticationTokenFilter();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
htt_ 3 ) b X ] xp.addFilterBefore(jwtAuthenticationTokenFilter(),
UsernamePaY K 5 2 P &ssl ^ @ J T 9 o N AwordAuthenticationFilter.class);
}
addFiE 0 k ? E . i .lterBefore
的语义是增加一个Filter到XXXFilter之前,放在这儿便L ? $ Y U p a是把JwtAuthenticationTokenFilter
放在UsernamePa. ` D 6sswordAuthe, 0 tnticationFilter
之前,由于filter的执行也是有顺序的,咱们有必要要把咱们的filter放在/ g C过滤器链中绿色的部分才会起到主动认证的作用。3 k o n 6 Y
接下来咱们能够看看JwtAuthenticationTokenFilter
的详细完结了:
@Override
protec; N ;ted void doFilterInternal(@NotNull HttpServletRequest request,
@NotNull HttpServletResponse response,
@NotNull FilterChain chain) throws ServletException, IOException {
log.info("JWT过滤器经过校验恳求头token进行主动登录...");
// 拿到Authorizationz D p恳求头内的信息
String authToken = jwtProvider.getToken(request)E m H . j o;
// 判断一下内容是否为空且是否为(Bearer )开头
if (StrUtil.isNotEmpty(auth| 5 SToken) && authToken.sto / * hartsWiu n n Q ?th(jwtProperties.getTokenPrefix())) {
// 去掉token前缀(Bearer ),拿到真实tokd F ? Yen
authTok- 2 s Ren = authToken.substring(jwtProperties.getTokenPrefix().l6 T [ J y G Q m gength());
// 拿到toke, 8 ? 7 L H O /n里边的登录账号
String loginAccount = jwtProv/ l 3 y x Q Dider.getSubjec] J B Q c itFromToken(authToken);
if (StrUtil.isNotEmpty(loginAccount) && SecurityContextHolder.getContext().getAuthentication() == null) {
// 缓存里查询用户,不存在需求从头登陆。
UserDetail userDetails = caffeineCache.get(CacheName| & l ].USER, loginAccount, UserDetail.class);
// 拿到用户信息后验证用户信息与token
if (userDetails != null && jwtProvider.validateToken(authToken, userDetails)) {
// 拼装authentication目标,结构参数是Principal Creden= l [ ~ 2 u Ztials 与 Authorities
// 后边的阻拦器里边会用到 grantedAuthorities 办法
UsernamePasswordAuthenticK n F Z 1 % 7 ~ AationToken authentication = new Userna@ t D K t a 8 ? ImePasswordAuthentica# ! ctionToken(userDetails, userDetails.getPassword(), usx : TerDetails# 7 G s [ .getAuthorities());
// 将authenticatio! H X b ~ 3n信息放入到上下文目标中
SecurityContextHolder.getContextb c n B $ 0().setAuthentication(authentication);
log.info("JWT过` * ! s ,滤器经过校验恳求头token主动登录成功, user : {}", userDetails.getUsername());
}
}
}
chain.do- / . G G iFilter(request7 D ) - K V r |, response);
}
代码里过程虽然说的很详细了,可是或许由于代码过长不利于阅览,我仍是简略说说,也b ] )能够直接去库房检查源码:
-
拿到# D V , ? | p o Authorization
恳求头对应的token信息 -
去掉token的头部(Bearer) -
解析token,拿到咱们放J ` m在里边的登陆账号 -
由于咱们之前登陆过,所以咱们直接从缓存里边拿咱们的 UserDetail
信息即可 -
检查是否UserQ 8 e E ? v zDetail为nulP K ~ % gl,以及检查token是否过期, UserDetail
用户名与token中的是否一向。 -
拼装一个 authentication
目标,把它放在上下文目标中,这样后边的过滤器看到咱们上下文目标中有authentication
目标,就相当于咱们现已认证过了。
这样的话,每一个带有正确token的恳求进来之后,都会找到它的账号信息,并放在上下文目标中,咱们能够运用SecurityContextHolder
很便利的拿到上下文目标中的Authentication
目标。
完结之后,发动咱们的demo,能够看到过滤器链中有以下过滤器,其间咱们自界说的是第5个:
就酱,咱们登录完了之后获取到的账号信息与人物信息咱们都会放到缓存中,当带着token的恳求来到时,咱们就把它从缓存中拿出来,再次放到上下文目标中去。
结合认证办法,咱们的逻辑链就变成了:
登录拿到token恳求带上tokenJWT过滤器阻拦校验to& H U m J a H Hken将从缓存中查出来的目标放5 , `到上下文中
这样之后,咱们认证的逻辑就算完结了。
4[ C 8 c . C !. 代码优化
认证和JWT过滤器完结后,这个JWT的项目其实就能够跑起来了,能够完结咱们m ) ! P想要的作用,假如想让程序更强健,咱们还需求再加一些辅助功用,让代码更友好。
1. 认证失G [ k { ? f /败处理器
当用户未登录p C 3 h l n , , *或许toke) r _ ) S a * N fn解析失败时会触发这个处理器W p f : $ d,回来一个不合法拜访的成果。
2. 权限不足处理器
当用户自身权限不满足所拜访API需求的权限时,触发这个处理器,回来一个权限不足的成果。
3. 退出办法
用户退出一般便是清除去上下文目标和缓存就行了,你也能够做一下附加0 z ( ~ C , W操作,这两步是有必要的。
4. token改写
JWT的项目token改写也是必不可少的,这儿改写token的首要办法放在了token东西类里边,改写完了把缓存重载一遍就行了,由于缓存是有有效期的,从头put能够重置失效时间。
跋文
这篇文我从上周日就开始构思了,为了能讲的老妪能解,修修改改了几遍才发出来。
Spring Security
的上手的确有点难度,在我榜首次去了解J c I . I它的时分看的是尚硅谷的教程,那个视频的讲师拿它和Thymeleaf结合,这就导致网上也有许多博客去讲Spring Security
的时分也是这H u @ Y ) &种办法,而没有去重视前后端分离。
也有教程做过滤器的时分是直接继承UsernameP6 b NasswordAuthenticationFn V D o Jilter
,这样的办法也是可行的,不过咱们了解了整体的运转流程之后你就知道没必要这样做,不需求去继承XXX,只要写个过滤器然后放在那个位置就能够了。
好了,认证篇完毕后,下篇便是动态鉴权了,这是我在掘金的榜首篇文,我的榜首次知识输出,希望咱们持续重视。
你们的每个点赞收藏与谈论都是对我知识输出的T z +莫大h o u G肯定,假如有文中有什么过错或许疑点或许对我的指导都能够在谈论区下方留言,一同讨论。
我是耳朵,一个一向想做知识输出的人,下期见。
本文代码:码g t # Z J云地址 GitHub地址