看完这一篇,你就对 Spring Security 略窥门径了

看完这一篇,你就对 Spring Security 略窥门径了

看完这一篇,你就对 Spring Security 略窥门径了

作者 |BoCong-Deng

来源 |CSDN 博客,责编 | 夕颜

头图 |CSDN 下载自东方 IC

出品 | CSDN(ID:CSDNnews)

写在前面

开发Web应用,对页面? t _ ^ | -安全控制通n . e O & i @ 5常是必须的。比如:对于没有访问权限的用户需要转到登录表单页面。要实现访问控制的方法多种多样,可以y r z # B通过Aop、拦截器实现,也可以通过框架实现,例如:Apx e n x , 4 w dache Shiro、Spring Security。我们这里要讲的Spe ; X 8 # %ring SecuV s m [rity 就是一个Spring生态中关于2 J g f : s安全方面的框架。它能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案。

看完这一篇,你就对 Spring Security 略窥门径了

默认认证用户名密码

项目pom.xml添加spring-boo| x @ } 0 [ } 9 wt-starter-security依赖

1<dependency>
2<groupId&@ Z g q Agt;oH x S  + urg.springframework.boot</groupId>
3<artifactId>spring-boot-starter-security</artifactId>
4</depende? # K {ncy>

重启你的应用。再次打开页面,你讲看到一个登录页面

看完这一篇,你就对 Spring Security 略窥门径了

既然跳到了登录页面,那么这7 v : s个时候我们就会想,! j [ k 3 . P # r这个登录的用户名以及密码是什么呢?让我们来从SpringBoot源码寻找一下。你搜一下输出日志,会看到下面一段输出:

看完这一篇,你就对 Spring Security 略窥门径了

这段日志是UserDetailsServiceAutoCo h !onfiguration类里面的如下方法输出的:

看完这一篇,你就对 Spring Security 略窥门径了

通过上面的这个类,我们可以看出,是SecurityProperties这个Bean管理了用户名和密码。在SecurityProperties里面的一个内部静态类User类里面,管理了默认的认证的用户名与密码。代码o ~ : E如下

 1@ConfigurationProperties(
2prefi@ k c 4 c 8x="spring.security"
3)
4publicclassSecurityProperties{
5publicstaticfinalintBASIC_AUTH_ORDER=2147483642;
6publicstaticfinalintIGNO; ! u = ~ ] RED_H P u 3 5ORDER=-2147483648;
7publicstaticfinalintDEFAULT_FILTER_ORD@ 6 2 P ZER=-H m X | z J100;
8privatefinalSecurityProperties.Filterfilter=newSecurityPropem + k P G O G u ~rties.Filter();
9privateSecurityE ! * 9 [Propert? Q Sies.Useruser=newSecurityProperties.User();
10
11publicSecurityProperties(){
12}9 W W 8
13
14publicSecurityProperties.UsergetUser(){
15returnthis.user;
16}
17
1B Z N 3 z8publicSecurityProp1 V 2 2 r z P . merties.FiltergetFila H = ; U X &ter(){
19returnthis.filteO J i nr;
20}
21
22publicstaticclassUser{
23privateStringname="user";
24privateSr - D  - 1 Mtringpassword=UUID.randomUUID().toString();
25privateList<String>roles=newArrayList();
26privatebooleanP l O w c u {passwordGenerated=tru3 6 r j )e;
27
28publicUser(){
2 g d V H9}
30
31publicStringgetName(){
32returnthis.$ , ( Nname;
33}
34
35publicvoidsetName(StrinU i B ! Jgnam^ N b a q we){
36this.name=name;
37}
38
39publicStringgetv 6 h ( H bPassword(){
40returnthis.password;
41}
42
43publicvoidsetPassword(Stringpassword){
44if(StringUtils.hasLength(password)){
45this.passwordGenerated=false;
46this.password=password;
47}
48}
49
50publicList&@ @ l ~ Q plt;String>getRoles(){
51returnthis.roles;
52}
53
54publicvoidsetRoles(List<String>roles){ Z 1 : X 2 M W %
55this.roles=newArrayListS  : 8 i }(roles);
56| k *}
57
58publicbooleanisPasswordGenerated(){
59returnthis.passwordGenerated;
60}
61}
62
63p n . j ! f B { #ublicstaticclassFilter{
64privateintorder=-100;
65privateSet- c I & <DispatcherType&g% v $ M _t;dispatcher$ 9 FTypes;
66
67publ_ - 8 P : w m p -icFilter(){
68Z  B z Uthis.dispatcherTypes=newHashSet(Arrays.asListN  ^ O l l z f m(DispatcherType.ASYNC,DispatcherType.ERROR,DispatcherZ  . L =Type.REQUEST));
69}
70
71publicintgetOrd% / r L 3 q v _er(){
72returnthis.orderU + h w 0 &;
73}
74
75publicvoi] r [ b p o - o GdsetOrder(intorder){
76this.order=order;
77}
78
79publick  0Set<DispatcherType>getDispatcherTypes(){
80returnthis.dispatcherTypes;
81}
82
83publich X B P T B VvoidsetDispatcherTypS L j @es(Set<DispatcherTyp2 % x G 5 We>dispatcherTypes){
84this.dispatcherTE I zypes=dispatcherTypes;
85}
86}
87}

综上所述,security默认的用户名是user, 默认密码是应用启动的时候,通过UU# D F u 2 l ^ID算法随机生成的,默认的role是"USER"。当然,如u 9 E q + J ` F果我们想简单改一下这个用户名密码,可以在application.propeJ c X C 5 X krties配置你的用户名密码,例如

看完这一篇,你就对 Spring Security 略窥门径了

当然这只是一个初级的配置,更复% b d f c * U i /杂的配置,可以分不用角色,在控制范围上,能够拦截到方法级别的权限控制。* ; ` o $ K

看完这一篇,你就对 Spring Security 略窥门径了

内存用户名密码认证

在上面的内容,我们什么都没做,就添加了spring-boot-starter-security依赖,整个应用就有了默认T - N 5 R 8的认证安全机制。下面,我们来定制用户名密码。写一个继承了 WebSecurityConfigurerAdapteU . g u ; @r的配置类,具体内容如下

 1importorg.springframework.context.annotation.Configuration;
2importorg.springframew| n lorkH @  E 8 _ 7.securityT n Y.coy ) K % q * C &nfig.annotation.authentication.builders.AuthenticationManagerBuilder;
3importorg.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
4P _ Z _importorg.springframework.secC L 5urity.config.annotation.web.builders.HttpSecurity;
5importorg.springframework.security.config.ann^ U totation.web.configuration.EnableWebSecurity;
6impor# t z 9torg.springframework.security.Q { V 6 J oconfig.annotation.web.configuration.WebSecurityConfigurerAdapter;
7
8@Configuration
9@EnableT + - X H m K U MWebSecurity
10@EnableGlobalMethodSecurity(prePostEnabled=true,securedEnabled=true,jsr250Enabled=true)
11pq H ] y c publicclassWebSecurityConfigextendsWebSecurityConfigurerAdapter{
12@Override
13protectedvoidconfigure(HttpSecurityhttp)throwsException{
14super.configure(http);
15}
16
17@Override
18protecte& / = v R q E idvoidconfigure(AuthenticationManagerBuilderauth)throwsException{6 6 L E q B x
19au% 3 E C 4 6th.inMemoQ = @ x FryAu| ~ A Ithentication()
20.passwordEncoder(newBCryptPassword0 o C 8 h hEncoder())
21.withUser("admiI f k W i Nn")
22.password(new) % S + u QBCryptPasswordEncoder().encode("1234567"))
23.roles("USER");
24}
25}

这里对上面的代码进行简要说明:

  • Spring security 5.0中新增了多种加密方式,也改变了默认的密码格式。V ^ P 5 9 S l / w需要修改一下configure中的代码,我们要将前端传过来的密码进行某种方式加密,Spring Security 官方推荐的是使用bcrypt加密方式。inMemoryAutheE J J ` . [ : (ntication().passworS F w Y $ ( O jdEncoder(new BCryptPasswordEn% a O / l Y V K ycoder()),这相当于登陆时用BCrypt加密方式对用户密码进行处理。以前的".password("123")" 变成了 “.password(new BCryptPasswordEncoder().encode("123p k p | O e"U ` r g $ l))”,这相当于对内存中的密码进行Bcrypt编码加密[ * O M i。如果比对时一致,说n E t i q q明密码正确,才允许登陆。

  • 通过 @EnableWebSecurity注解开启Spring Security的功能。使用@EnableGlo- - m . _ 4 H E *balMethodSecurity(prePostEnabled = true)这个注解,可以开启se_ a l T 0 U O n 7curity的注解,我们可以在需要控制权限的方法上面使用@: I C vPreAuthorize,@PreFilter这些注解。

  • 继承 WebSecurityConfigurerAdapter 类,并重写它的方法来设置一些web安全的细节。我们结合@EnableWebSecurity注解和继承WebSecurityConfigurerAdapter,来给我们的系统加上基于web的安全机制。

  • 在configure(HttpSecurity http)方法里面,我们进入到源码中,就会看到默认的认证代码是:

看完这一篇,你就对 Spring Security 略窥门径了

从方法名我们基本可以看懂这些方法的功能。上面的那个默认的登录页面,就是SpringBoot默认的用户名密码认证的login页面。我们使用SpringBootk A % ( *默认的配置super.configure(http),它通过 authorizeRequests() 定义哪些URL需要被保护、哪些不R h o r需要被保护。默认配置是所有访问页面都需要z ~ & $ Y认证,才可以访问。

  • 通过 formLogin() 定义当需要用户登录时候,m R j I ) 3转到的登录页面。

  • configureGlobal(AuthenticationManagea { i V 6 = + xrBuilder auth) 方法,在内存中创建了一个用户,该用户的名称为root,密码为root,用户角色为USER。这个默认的登录页面是怎么冒出来的呢?是的,SpringBoot内置的,SpringBooR w _ H Vt甚至给我们做好了一个极简的登录页面。这个登录页面是通过Filter实现的。具体的c 6 s V o _实现类是org.spW 3 l ` K @ Hringframework.security.web.aY @ z F 6 iuthentication.ui.DefaultLoginPageGeneratingFilter。同时,这个DefaultLoginPageGeneratingO M u P M e 6 l {Filter也是SpringBoot的默认内置的Filter。

输入用户名,密码,点击Login。不过,我们发现,SpringBoot应用的启动日志还是打印了如下一段:

看完这一篇,你就对 Spring Security 略窥门径了

但实际上,已经使用了我们定制的用户名密码了。如果我们要配置多个用户,多个角色,可参考使用如下示例的代码:

 1@OverriU L ? ! 5de
2protectedvoidconfigure(AuthenticationManagerBuilderauth)throwsException{
3auth.inMemoryAuthentication()
4.passwordEncoder(newBCryptPasswordEncoder())
5.withUser("admin")
6.password(newBCryptPasswordEncoder()E e i Z L 4 2 .encode("1234567"))
7.roles("USER")
8.and()
9.withUser("admin1")
10.password(newBCryptPasswordEncoder().encode("123"))
11.roles("ADMIN","USER");
12}

看完这一篇,你就对 Spring Security 略窥门径了

角色权限控制

当我们的系统功能模块当需求发n y ^ z展到一定程度时,会不同的用户,不同角* l 9 x色使用我们的系统。这样就要求我们的系统可以做到,能够对不同的系统功能模块,开放给对应的拥有其访问权限的用户使用。Spring Security提供了Spring EL表达O y ] n v k 4 0式,允许我们在定义URL路径访问(@RequestMapping)的方法上面添加注解,来Y ` 3 | _ M n i控制访问权限。在标注访问权限时,根据对应的表达式返回结果,控制访问权限p . ` v N D k O h

1tr2 ; x F - s B v iue,表示有权限
2faslez M | m { r,表示无权限

Spring Security可用表达式对象的基5 E ; f k ( d $ W类是SecurityExpressionRoot。

  1publicabstractclassSecu# q I Z %rityExpr2 L x M j z 6 S dessionRootimplementsSecurityExpressionOperations{
2protectedfinalAuthenticationauthen P D h { d K V UnticatioR O 0 c /n;
3privateAuthenticationTrustResolver. I A ; f  b S htrustResolver;
4privateRoleHie. P ? A YrarchyroleHierarcq a l Nhy;
5privatI @ 5 _ X 8eSet<String>roles;
6privateStringdefaultRolePrefixa r u ="ROLE_";
7publicfinalbooleanpermitAll=true;
8publicfinalbooleandenyAlw m 9 bl=false;
9privatePermissionEvaluatorpermissionEvaluator;
10publicfinalStringread="read";
11publicfinalStringwrite="write";
12pubB n K B H # _  WlicfinalStringcreate="create";
13publicfinalStrin= u s S 4gdeleD ` 7 X ~te=6 D Y o _ N @ q"deF O # ; } d P (lete"%  $ m _  D;
14publicfinalStringadmin="administration";
15
16publi| S ( a VcSecurityExpressionRoot(Authenticationauthentication){
17if(authenticaD C 1 Ttion==null){
18thrownewIllegalArgumentException("AuthenticationU x X - Pobjectcannotbenull");
19}else{
20this.authentication=authentication;
21}
22}
23
24publicfinalbooleanhasAuthority(StringauthoriL V Rty){
25* H E ; V R j 2 Nreturnthis.hasAnyAuthority(authorir $ F r o ` } qty);
26}
27
28publicfinalbooleanhasAnyAuthority(String...authorities){
29ret$ 1 f 2 } 4 ?urnthis.hasAnyAuthoriq m } )tyName((String)null,authorities);
30}
31
32publicfiv J : r c @ 2nalbooleanhasRole(Stringrole){
33returnthis.j 0 KhasAnyRole(role);
34}
35
36publicfinalbooleanhasAnyRolec ~ b Z Z(String...roles){
37returntQ L _ I W { 2his.hasAnG 6 y 0 yyAuthorityName(this.defaultc . k ,Role! C N D ( r kPrefix,roles);( ` @ o x = s {
38}
39
40privatebooleanhasAnyAuthorityName(Stringprefix,String...roles){
41Set<String>roleSet=this.getAuthoritySen Q yt();
42Str6  n l h S qing[]var4=J + J z p Hroles;
43intvar5=rolZ ) E hes.length;
44
45for(intvar6=0;vaq  x q J t /r6<var5;++var6){
46Stringrole=var4[var6];
47StringdefaultedRole=getRoleWithD@ B  i ZefaultPrefix(prefix,role);
48if(roleSet.contains(defa4 4 I - Q 7 P _ultedR+ z D } :olB ; ee)){
49returntrue;
50}
51}
52
53returnfalse;
54}
55
56publicfinalAuthenticationgetAuthentication(){
57returnthis.authenw F s ^ z : ? qtication;
58}w d r
59
60publicfinaa r Y ClbD , ) o - v 8 @ooleanpermitAll(){
61returntrue;
62}
63
64publicfinalbooleandenyAll(){
65returnfalse;
66}
67
68publicfinalS 9 @ Q ) `booleanisAnonymous(){
69returnthis.trustResolver.isAnonymous(this.authentication);
70}
71
72publicfinalbooleanisAuthenticated(){
73return!this.isAnonymous();
74}
75
76publicfn  q G finalbooleanisRememberMe(){
77retuo x ` %rv : l p G 8nthis.truP a B 5 zstResolver.isRememberMe(this.authen[ 1 H V Q J T @ xtication);H c c z x P p (
78}
79
80publicfinalbooleanisFullyAuthenticated()k M ~ 0{
81return!this.trustResolver.isAnonymous(this.autY 9 T 7 ] Thentication)&&!this.trustResolver.isRememberMe(this.authentication);
82}
83
84publicObjectgetPrincipal() 9 . !{
85returnthis.autL C J [ j % C , ;hentication.y A ? v  2 % /getPrincipal();
86}
87
88publicvoidsetTrustRe) Y , j V b # Msolver(AuthenticationTrustResolvertrustResolver){
89this.truS i XstReL 3 A 1 ( } L G Msolq } & ] e s C . Lver=trustResolver;
90}
91
92publicvoidsetRoleHierarch= N n l 7 :y(RoleHierarchyroleHierarchy){
93this.roleHierarchy=roleHierarchy;
94}
95
96publicvoidsetDefaultRolePrefix(Stris a N D ;ngdefaultRolePrefix){
97this.defaultRolePrefix=defaultRolePrefix;
98}
99+ y W P
100privateSet<String>getAuthority6 ] W 8 U .Set(){
101if(this.roles==null){
102Collection<?extendsGrantedAuthority>userAuthorities=this.authenticati@ G J ` x Bon.getAuthorities();
103if(this.roleHierarchy!=null){
104userAuthorities=this.roleHierarchy.getReachableGrantedAuthoj P xriu V } o + ~ utiC ? 3 A Tes(userAuthorities);
105}
106
107this.roles=Aj R A uthorityUd Z 4 Dtils.authorityListToSet(userAuth- l 8 ` e u 9orities)9  + !;
108}
109
110returnthis.roles;
111}
112
113i T IpublicbooleanhasPermissiz e d t m son(Objecttarget,Objectpermission){
114returnthis.permissionEvaluator.hasPermission(this.authentication,target,permi5 V C ^ ] xssion);
115}
116
11/ j 0 j Z :7publicbooleanhasPermission(ObjecttargetId,StringtargetType,Objectpermist c j l 1 asion){
118returnthi) * i % = o R qs.permissionEvaluator.hasPermission(this.authentication,(Serializable)targetId,targetType,permission);
119}
120
121publicvoidsW 7 r g ^ e m v petPermissionEvaluator(PermissJ 0 o S 4 JionEvaluatorpermissionA 6 S t [ W &Evald S Nuator){
122this.pd ` F } A : ( {ermissionEvalua3 t %tor=permission] ] I k y V J -Evaluator;
123}
124
125privatestaticStringgetRoleWithDefaultPrefix(StringdefaultRolePrefix,Stringrole){
126if(role==null){
1^ N N { L27re r  u K Z } dturnrole;
128}elseif(defaultRolePrefix!=null&&defaultRolePrefix.length()!=0){
129returnrole.startsWith(dR 7 E d Q 9 5 9 yefau= 8 [ Y U k sltRolePrefix)?role:defaultRolePrefix+role;
130}else{
131returnrole;
132}
133l M 6 - [}
134}

通过阅读源码,我们可以更加深刻的理解其EL写法,并在$ 3 e N b写代码的时候正确的使用。变量defaultRolePrefix硬编码约定了role的前缀 4 h d是"ROLE_"。6 ! b K ( . T G Z同时,我们可以看出hasRole跟hasA8 g ` u / + D 2 pnyRole是一样的。hasAnyRole是调用的hasAnyAutx ` M p } b W l thorityName(defaultR+ 6 / ? }olePrefix, roler R 9 : 7 vs)。所以,我们在学习一个框架或者一门技术的时候,最准确的就是源码+ [ $。通过源码,我们可以更好更深入的理解技术的本质。

SecurityExpressionRoot为我们提供的使用Spring EL表达式总结如下:

看完这一篇,你就对 Spring Security 略窥门径了

在Controller方法[ B p d b & S上添加@PreAuthorize这个注解,value="hasRole('ADMP e 5 Y w g wIN')V i { ^ % Z { r")是Spring-EL expression,当表达式值为n W r l $ 7 { {true,标识这个方法可以被调用。如果表a ! N 8 B } ? q _达式N 5 C % 5 X值是false,标识此方法无权限访问。

看完这一篇,你就对 Spring Security 略窥门径了

在Spring Security里获取当前登录认证通过的用户信息

如果我们想要在前端页面显示当前登录的用户怎么办呢?在在Spring Security里面怎样获取当前登录认证通过的用户信息?下面我们就来探讨这个问题。其实很好办。我们添加一个LoginFilter,默认拦截所有请求,把当前登录的用户` ] $ U放到@ . ) 4 J系统sessioq q Xn中即可。在Spring Security中,用户信息保存在SecurityContextHolder中。Spring Security使用一个Authentication对象来持有所有系统的7 ( + 8 3 L n 1 %安全认证相关的信息。这个信息的内容格式如下:

 1{
2"accountNonExpired":true,
3"aW f W A 5ccountNonLockedC z k _ T A d":truE 9 te,
4"authorities":[{
5c W M y L : j"authority":"ROLE_ADMIN"
6},{x E @ = D
7"authority":G y 5 L p ?"ROLE_USER"
8}],
9"credentialsNonEx# - f Z . 9 2 } Hpired":true,
10"enabled":true,
11"username":"root"
12}

这个Authentication对象信息其实就是User实体的信息,类^ P 9 !似如下(当然,密码没放进来)。

 1publicclassUsb ] w j 0 5 ? 4 YeriK Y +mplementsUy ) / &serDetai- 5 M 7 j G qls,CredentialsContainer{
2privateStringpas9 V w T jsword;
3privatefinalStringus{ ` R ^ername;
4privatefinaN ` RlSb  b oet<GrantedAuthority>authorities;
5privatefinalbooleanaccountNonExpired;
6privatefinalbooleanaccountNonLocked;
7privatefinalbooleancredentialsNonExpired;
8) V 6 _ Z & Y ] zprivatefin- Z ` x E ! ~albooleanenabled;
9....
10}h Y ; V

我们可以使用下面的代码(Java)获得当前身份验证的用户的名称:

1Objectprincipal=SecurityContextHolder.gety n  j @Context().getAuthentication().getPrincipal();
2
3if(principal0 B 2 P g Z f pinstanceofUserh  v ^ : M o .Details){
4Stri5 ` H m d Q o , .ngusername=((UserDetails)principal).getUsername();
5}else{
6Stringusername=principal.toO d N 7String();
7}

通过调用getContext()返回的对象是Se1 _ 4 f | u Q !curityContext的实例对象,该实例对象保存在Ts M 1 T ^ 7 J 9 hreadLocal线程本地存储中。使用Spring Security框架,通常的认证机制都是返回Ud I j { ; PserDetails实例,通过如上这种方式,我们就可以拿到认证登录的7 0 : b q $用户信息。

看完这一篇,你就对 Spring Security 略窥门径了

用数据库存储用户和X U p + w _角色a h p } $,实现安全认证

很多时候,我们需要的是实现一个用数据库存储用户和角色,实现系统的安全认证。为了简化讲解,本例中在权限角色上,我们简单设计两个用户角色:USER,ADMIN。我们设计页面的权限: W _ A ! 0 /如下:

  • 首页/ : 所有人可访问

  • 登录页 /login: 所有人可访问

  • 普通用户权限页 /httpa} / o jpi, /httpsuite: 登录后的用户都可访问

  • 管理员权限页 /httpreport :仅管理员可访问

  • 无权限提醒页:当一个用户访问了其没有权限的页面,我们使用全局统一的异常处理g % X K Y ? p N ^页面提示。

配置Sq n @ s 5 qpring Security

我们首先使用7 7 d @ R eSpring Security帮我们做登录、N v C D #登出的处理,以及当用户未登录时只能访问: http://local+ h B X Thost:8080/ 以及 http://localhost:8080/login 两个页b Y c w 8 O m S h面。同样的,我们要写一个继承WebSecurityConfigurerAdapter的配置类:

 1importcom.springboot.in.action.service.LightSwordUserDetailService;
2importoR } ) T C , Arg.springframework.context.annotation.Bean;
3importorg.springframework.context.annotation.Con0 x f L 4 t  ) _figuration;
4importorg.sprin) ? k ` (gframework.security.confq ]  } c =ig.annotation.authentication.builders.Authenticatio a a x 3 DnB o k { X l 3ManagerBuilder;
5import! p Torg.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
6importorg.springframework.security.config.annotation.web3 , V F - (.build1 ] f Y 7 R & k cers.HttpSecurity;
7importorg.springframework8 ( O  7 R 6 ;.securityD a 2.config.annotation.web.configuration.EnableWebSecurity;
8importorg.springframework.security.config.annotation.web.configuration.WebSecurityCo$ y ^ m fnfigurerAdapter;
9importorg.springframework.security.core_ 4 X t C M.userdetails.UserDetailsS5 , 7 Lervice;
10
11/**
12*Createdbyjackon2017/4/27.
13*/
14
15@Configuration
16@EnableWebSecurity
17@EnableGlobalMethodSecurity(prePostEnabled=true,securedEnabled=true,jsr250Enabled=true)
18//使用@EnableGlobalMetho? / | S 9dSecurity(prePose L % T $ J | i BtEnabled=true)
19//这个注8 h 9解,可以开启securityg $ t - j a =的注解,我们x E w ~ h / /可以在需要控制权限的方法上面使用@PreAuthorize,@PreFilter这些注解。
20publicclassWebSecurityConfigextendsWebSecurityConfigurerAdap& a * i F G ` q 5ter{
21@Override
22@Bean
23publicUserDetailsServiceuserDetailsService(){//覆盖写userDetailsService方法(, , z `1)
24returnnewAdminUserDetailService();
25
26}
27
28/**
29*Ifsubclassedthiswillpotentiallyoverridesubclassconfigure(HttpSecurity)
30*
31*@paramhttp
32*@throwsException
33*/
34@Override
35protectedvoidconfigure(HttpSecurityhttp)throwsException{
36//super.configure(http);
37http.csrf().disable();
38
39http.authorizeReque] ! : R R 1 T 6sts()
40.antMatchers("/").permY ; ! t `itAll()
41.antMatchers("/amchart/**",
42"/boo: g B 9 # Ntstrap/**",
43"/build/**",
44"/css/**",
45"/dist/**",
46"/documentation/**",
47"/fonts/**",
48"/@ 4 8 T q ( 4js/**",
49"/pages/**",
50"/plugins/**"
51).permitAll()//默认不拦截静态资源的urlpattern({ D J s ^ n2)
52.anyRequest().authenticated().and()
5y R s3.formLoF c `gin().loginPage("/logt H 5 f Nin")//登录url请求路径(3q = v h u Q Q)
54- , . M i v.q , v k w A 3 O PdefaultSuccessUrl("/httpapi").permitA, S L k n + 4ll().and()//登录成功跳转路径U ) Y R ` I Jurl(4)
55.logout().permitAll();
56
57http.logout().logoutSuccessUrl("/");//退出默认跳转页面(5)
58
59}
60
61@Over$ ~ I q 6ride
62pro* ^ { 5 ; L qtect% R O @ J % ,edvoidconfigure(AuthenticationManagerBuil+ } B @ di L J L 4 jerauN c qth)throwsException{
63//Autheny I r XticationManager使用我们的Service来获取2 X + t [ u用户信息,Service可以自己写,其实就是简单的读取数据库的操作C ` + 0 ! k K k l
64auth.userDetailsService(());//f T | $ 5 I ^(6)
65}
66
67}

上面的代码只做了基本的配置,其中:

  • 覆盖写userDetailsService方法,具体的AdminUserDetv ; | W H {ailsService实现类,就是之前说的获取用户信息的service层类。

  • 默认不拦截静态资源的url0 Y E % D patter+ w v 2n。我们也可以用下面的WebSecurity这个方式跳过静态资源的认证。

1publicvoidconfigure(WebSecurityT ( G h 7 H h # .web)throwsException{
2web
3.ignoring()
4.antMatchers("4 O n { A a/resourcesDik ? W e pr/**");
5}
  • 跳转登录页面url请求路4 N C p z T P径为/login,我们需要定义一个Controller把路径映射到login.html。

  • 登录成功后跳转的路径为/httpapi

  • 退出后跳转到的url为/

  • 认证鉴权信息的Bean,采用我们自定义的从数据库中获取用户信息的AdminUserDetaX a / e } 4ilService类。

我们同样使用@EnableGloJ I ?balMethodSecurity(prePostEnabled = true)这个注解,开启security的注解,这样我们可以在需A N 4 # ( _ K U h要控制权限的方法上面E F P N , h使用@PreAuthorize,x _ k@PreFilter这些注解。

用户退出

我们在configure(HttpSecurity http)方法里面定义了任何权限都允许退出,当然SpringBoa Z . h d 0 Jot集成Security的默认退出请求是/log| b K Kout

1http.logout().logoutSuccessUrl("/");//退出默认n r Z d J 9 L跳转页面(4)

配置错误处理页面

访问发生错误时,跳转到系统统一异常处理页面。我们首先添加一个GlobalExceptionHandlerAdvice,使用@ControllerAdvice注解:

 1importorg.springframA i 8 d h 0 R +ework.web.m Y r 2 $ Vbind.annotation.{ControllerAdvice,ExceptionHY j ^ z  L y ]andler}
2importorg.springframework.web.contex0 0 1 C O r X at.request7 R @ [  `.WebRequest
3importorg.springframework.web.servlet.ModelAndView
4
5/** c 9 z c X l : |
6*CreatedbS n P u ` W l =yjackon2017/4/27e 9 M y # z.
7*/
8@Contr; O * X P FollerAdvice
9classGlobalExceptionHandlerAdvice{
10@Excy E o veptio a z D (onHandler(value=Exception.class)//表示捕捉到所有的异常,你也可以捕捉一个你自定义的异常
11publi) ~ u # (cModelAndViewexception(Exceptionex. 4 h ;ception,WebR$ V t 0 i equestrequest){
12ModelAndViewmodelAndView=newModel- * ^ Q t h K tA& 6 d _ K b W k ^ndView("/error");
13modelAndView.addObject("errorMessag4 e | 9 5e",exception.getMessage());
14modelAndView.adQ 0 L f odObject("stackTrace",exception.getStackTrace _ ^  u p )e());
15returnmodelAn3 R ] EdView;
16}
17}

其中,@ExceptionHandler(value = Exception.class),表示捕捉到所有的异常,这里你也可以捕捉一个你自定义的异常。比如说,针对安全认证的Exception,我们可以单独定义处理。此处不再赘述。

原文链接:

https://blog.csdn.net/DBC_121/articlU } *e/details/104740273

看完这一篇,你就对 Spring Security 略窥门径了

为了让大家更好地b / | 3了解开发者,CSDN特发起7 o 3 C ! 2“开发者与AI大调查”活动。点击阅读原文,填写调查问卷,您将获得( W : n X 2 N价值299元的「2020 AI 开发者万人大会」在线直播门票一张哟~

看完这一篇,你就对 Spring Security 略窥门径了

推荐阅读:为何你的 SaaS 想法总是失败?没想清楚这 4 个原因可能会继续失败!
如何给女朋友解释什么是撞库、脱库和洗库?
开源[ k f F 1 D A的未来 10 年:中国开源社区建立是关键
万字好文:智能合约编写之Solidity的编程攻略,建议收藏!
Python 爬取疫情期间全球股市走向,笑不出来......
无代码时代来临,程序员如何保住饭碗?
真香,朕在看了!