Spring Boot集成Spring Security

8.6 Spring Boot集成Spring Security

开发Web应用,对页面的安全控制通常是必须的。比如:对于没有访问权限的用户需要转到登录表单页面。要实现访问控制的方法多种多样,可以通过Aop、拦截器实现,也可以通过框架实现,例如:Apache Shiro、Spring Security。

很多成熟的大公司都会有专门针对用户管理方面有一套完整的SSO(单点登录),ACL(权限访问控制),UC(用户中心)系统。 但是在我们开发中小型系统的时候,往往还是优先选择轻量级可用的业内通用的框架解决方案。

Spring Security 就是一个Spring生态中关于安全方面的框架。它能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案。

Spring Security,是一个基于Spring AOP和Servlet过滤器的安全框架。它提供全面的安全性解决方案,同时在Web请求级和方法调用级处理身份确认和授权。在Spring Framework基础上,Spring Security充分利用了依赖注入(DI,Dependency Injection)和面向切面技术。

Spring Security提供了一组可以在Spring应用上下文中配置的Bean,充分利用了Spring IoC(Inversion of Control, 控制反转),DI和AOP(Aspect Oriented Progamming ,面向切面编程)功能,为应用系统提供声明式的安全访问控制功能,减少了为企业系统安全控制编写大量重复代码的工作,为基于J2EE企业应用软件提供了全面安全服务[0]。Spring Security的前身是 Acegi Security 。

本章节使用SpringBoot集成Spring Security开发一个LightSword接口自动化测试平台,由浅入深的讲解SpringBoot集成Spring Security开发技术知识。

本章节采用SpringBoot集成的主要的后端技术框架:

编程语言:java,scala
ORM框架:jpa
View模板引擎:velocity
安全框架:spring security
数据库:mysql

初阶 Security: 默认认证用户名密码

项目pom.xml添加spring-boot-starter-security依赖


   
   
  1. <dependency>
  2. <groupId>org.springframework.boot </groupId>
  3. <artifactId>spring-boot-starter-security </artifactId>
  4. </dependency>
  • 1
  • 2
  • 3
  • 4

重启你的应用。再次打开页面,你讲看到一个alert表单对话框:

1240

这个用户名,密码是什么呢?

让我们来从SpringBoot源码寻找一下。

你搜一下输出日志,会看到下面一段输出:


   
   
  1. 2017 -04 -27 21: 39: 20.321 INFO 94124 --- [ost-startStop-1] b.a.s.AuthenticationManagerConfiguration :
  2. Using default security password: 6c920ced -f1c1 -4604 -96f7 -f0ce4e46f5d4
  • 1
  • 2
  • 3

这段日志是AuthenticationManagerConfiguration类里面的如下方法输出的:


   
   
  1. @Override
  2. public void configure( AuthenticationManagerBuilder auth) throws Exception {
  3. if (auth. isConfigured()) {
  4. return;
  5. }
  6. User user = this. securityProperties. getUser();
  7. if (user. isDefaultPassword()) {
  8. logger. info( String. format( "%n%nUsing default security password: %s%n",
  9. user. getPassword()));
  10. }
  11. Set< String> roles = new LinkedHashSet<>(user. getRole());
  12. withUser(user. getName()). password(user. getPassword())
  13. . roles(roles. toArray( new String[roles. size()]));
  14. setField(auth, "defaultUserDetailsService", getUserDetailsService());
  15. super. configure(auth);
  16. }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

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


   
   
  1. public static class User {
  2. /**
  3. * Default user name.
  4. */
  5. private String name = "user";
  6. /**
  7. * Password for the default user name.
  8. */
  9. private String password = UUID. randomUUID(). toString();
  10. /**
  11. * Granted roles for the default user name.
  12. */
  13. private List< String> role = new ArrayList<>( Collections. singletonList( "USER"));
  14. private boolean defaultPassword = true;
  15. public String getName( ) {
  16. return this. name;
  17. }
  18. public void setName( String name) {
  19. this. name = name;
  20. }
  21. public String getPassword( ) {
  22. return this. password;
  23. }
  24. public void setPassword( String password) {
  25. if (password. startsWith( "${") && password. endsWith( "}")
  26. || ! StringUtils. hasLength(password)) {
  27. return;
  28. }
  29. this. defaultPassword = false;
  30. this. password = password;
  31. }
  32. public List< String> getRole( ) {
  33. return this. role;
  34. }
  35. public void setRole( List<String> role) {
  36. this. role = new ArrayList<>(role);
  37. }
  38. public boolean isDefaultPassword( ) {
  39. return this. defaultPassword;
  40. }
  41. }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53

综上所述,security默认的用户名是user, 默认密码是应用启动的时候,通过UUID算法随机生成的。默认的role是"USER"。

当然,如果我们想简单改一下这个用户名密码,可以在application.properties配置你的用户名密码,例如


   
   
  1. # security
  2. security.user.name=admin
  3. security.user.password=admin
  • 1
  • 2
  • 3

当然这只是一个初级的配置,更复杂的配置,可以分不用角色,在控制范围上,能够拦截到方法级别的权限控制。 且看下文分解。

中阶 Security:内存用户名密码认证

在上面章节,我们什么都没做,就添加了spring-boot-starter-security依赖,整个应用就有了默认的认证安全机制。下面,我们来定制用户名密码。

写一个extends WebSecurityConfigurerAdapter的配置类:


   
   
  1. package com.springboot.in.action.security;
  2. import org.springframework.context.annotation. Configuration;
  3. import org.springframework.security.config.annotation.authentication.builders. AuthenticationManagerBuilder;
  4. import org.springframework.security.config.annotation.method.configuration. EnableGlobalMethodSecurity;
  5. import org.springframework.security.config.annotation.web.builders. HttpSecurity;
  6. import org.springframework.security.config.annotation.web.configuration. EnableWebSecurity;
  7. import org.springframework.security.config.annotation.web.configuration. WebSecurityConfigurerAdapter;
  8. /**
  9. * Created by jack on 2017/4/27.
  10. */
  11. @Configuration
  12. @EnableWebSecurity
  13. @EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
  14. public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
  15. @Override
  16. protected void configure( HttpSecurity http) throws Exception {
  17. super.configure(http);
  18. }
  19. @Override
  20. protected void configure( AuthenticationManagerBuilder auth) throws Exception {
  21. auth
  22. .inMemoryAuthentication()
  23. .withUser( "root")
  24. .password( "root")
  25. .roles( "USER");
  26. }
  27. }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

简要说明:

1.通过 @EnableWebSecurity注解开启Spring Security的功能。使用@EnableGlobalMethodSecurity(prePostEnabled = true)这个注解,可以开启security的注解,我们可以在需要控制权限的方法上面使用@PreAuthorize,@PreFilter这些注解。

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

3.在configure(HttpSecurity http)方法里面,默认的认证代码是:


   
   
  1. http
  2. .authorizeRequests()
  3. .anyRequest() .authenticated()
  4. .and()
  5. .formLogin() .and()
  6. .httpBasic();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

从方法名我们基本可以看懂这些方法的功能。上面的那个默认的登录页面,就是SpringBoot默认的用户名密码认证的login页面。其源代码如下:


   
   
  1. <html><head><title>Login Page</title></head><body onload='document.f.username.focus();'>
  2. <h3>Login with Username and Password </h3> <form name='f' action='/login' method='POST'>
  3. <table>
  4. <tr> <td>User: </td> <td> <input type='text' name='username' value=''> </td> </tr>
  5. <tr> <td>Password: </td> <td> <input type='password' name='password'/> </td> </tr>
  6. <tr> <td colspan='2'> <input name="submit" type="submit" value="Login"/> </td> </tr>
  7. <input name="_csrf" type="hidden" value="b2155184-80cf-48a2-b547-91bbe364c98e" />
  8. </table>
  9. </form> </body> </html>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

我们使用SpringBoot默认的配置super.configure(http),它通过 authorizeRequests() 定义哪些URL需要被保护、哪些不需要被保护。默认配置是所有访问页面都需要认证,才可以访问。

4.通过 formLogin() 定义当需要用户登录时候,转到的登录页面。

5.configureGlobal(AuthenticationManagerBuilder auth) 方法,在内存中创建了一个用户,该用户的名称为root,密码为root,用户角色为USER。

我们再次启动应用,访问 http://localhost:8888
页面自动跳转到: http://localhost:8888/login
如下图所示:

1240

这个默认的登录页面是怎么冒出来的呢?是的,SpringBoot内置的,SpringBoot甚至给我们做好了一个极简的登录页面。这个登录页面是通过Filter实现的。具体的实现类是org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter。同时,这个DefaultLoginPageGeneratingFilter也是SpringBoot的默认内置的Filter。

输入用户名,密码,点击Login

1240

成功跳转我们之前要访问的页面:

1240

不过,我们发现,SpringBoot应用的启动日志还是打印了如下一段:


   
   
  1. 2017 -04 -27 22: 51: 44.059 INFO 95039 --- [ost-startStop-1] b.a.s.AuthenticationManagerConfiguration :
  2. Using default security password: 5fadfb54 -2096 -4a0b -ad46 -2dad3220c825
  • 1
  • 2
  • 3

但实际上,已经使用了我们定制的用户名密码了。

如果我们要配置多个用户,多个角色,可参考使用如下示例的代码:


   
   
  1. @Override
  2. protected void configure(AuthenticationManagerBuilder auth) throws Exception {
  3. auth
  4. .inMemoryAuthentication()
  5. .withUser( "root")
  6. . password( "root")
  7. . roles( "USER")
  8. . and()
  9. .withUser( "admin"). password( "admin")
  10. . roles( "ADMIN", "USER")
  11. . and()
  12. .withUser( "user"). password( "user")
  13. . roles( "USER");
  14. }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

角色权限控制

当我们的系统功能模块当需求发展到一定程度时,会不同的用户,不同角色使用我们的系统。这样就要求我们的系统可以做到,能够对不同的系统功能模块,开放给对应的拥有其访问权限的用户使用。

Spring Security提供了Spring EL表达式,允许我们在定义URL路径访问(@RequestMapping)的方法上面添加注解,来控制访问权限。

在标注访问权限时,根据对应的表达式返回结果,控制访问权限:

true,表示有权限
fasle,表示无权限

Spring Security可用表达式对象的基类是SecurityExpressionRoot。


   
   
  1. package org. springframework. security. access. expression;
  2. import java. io. Serializable;
  3. import java. util. Collection;
  4. import java. util. HashSet;
  5. import java. util. Set;
  6. import org. springframework. security. access. PermissionEvaluator;
  7. import org. springframework. security. access. hierarchicalroles. RoleHierarchy;
  8. import org. springframework. security. authentication. AuthenticationTrustResolver;
  9. import org. springframework. security. core. Authentication;
  10. import org. springframework. security. core. GrantedAuthority;
  11. import org. springframework. security. core. authority. AuthorityUtils;
  12. /**
  13. * Base root object for use in Spring Security expression evaluations.
  14. *
  15. * @author Luke Taylor
  16. * @since 3.0
  17. */
  18. public abstract class SecurityExpressionRoot implements SecurityExpressionOperations {
  19. protected final Authentication authentication;
  20. private AuthenticationTrustResolver trustResolver;
  21. private RoleHierarchy roleHierarchy;
  22. private Set< String> roles;
  23. private String defaultRolePrefix = "ROLE_";
  24. /** Allows "permitAll" expression */
  25. public final boolean permitAll = true;
  26. /** Allows "denyAll" expression */
  27. public final boolean denyAll = false;
  28. private PermissionEvaluator permissionEvaluator;
  29. public final String read = "read";
  30. public final String write = "write";
  31. public final String create = "create";
  32. public final String delete = "delete";
  33. public final String admin = "administration";
  34. /**
  35. * Creates a new instance
  36. * @param authentication the {@link Authentication} to use. Cannot be null.
  37. */
  38. public SecurityExpressionRoot( Authentication authentication) {
  39. if (authentication == null) {
  40. throw new IllegalArgumentException( "Authentication object cannot be null");
  41. }
  42. this. authentication = authentication;
  43. }
  44. public final boolean hasAuthority( String authority) {
  45. return hasAnyAuthority(authority);
  46. }
  47. public final boolean hasAnyAuthority( String... authorities) {
  48. return hasAnyAuthorityName( null, authorities);
  49. }
  50. public final boolean hasRole( String role) {
  51. return hasAnyRole(role);
  52. }
  53. public final boolean hasAnyRole( String... roles) {
  54. return hasAnyAuthorityName(defaultRolePrefix, roles);
  55. }
  56. private boolean hasAnyAuthorityName( String prefix, String... roles) {
  57. Set< String> roleSet = getAuthoritySet();
  58. for ( String role : roles) {
  59. String defaultedRole = getRoleWithDefaultPrefix(prefix, role);
  60. if (roleSet. contains(defaultedRole)) {
  61. return true;
  62. }
  63. }
  64. return false;
  65. }
  66. public final Authentication getAuthentication( ) {
  67. return authentication;
  68. }
  69. public final boolean permitAll( ) {
  70. return true;
  71. }
  72. public final boolean denyAll( ) {
  73. return false;
  74. }
  75. public final boolean isAnonymous( ) {
  76. return trustResolver. isAnonymous(authentication);
  77. }
  78. public final boolean isAuthenticated( ) {
  79. return ! isAnonymous();
  80. }
  81. public final boolean isRememberMe( ) {
  82. return trustResolver. isRememberMe(authentication);
  83. }
  84. public final boolean isFullyAuthenticated( ) {
  85. return !trustResolver. isAnonymous(authentication)
  86. && !trustResolver. isRememberMe(authentication);
  87. }
  88. /**
  89. * Convenience method to access {@link Authentication#getPrincipal()} from
  90. * {@link #getAuthentication()}
  91. * @return
  92. */
  93. public Object getPrincipal( ) {
  94. return authentication. getPrincipal();
  95. }
  96. public void setTrustResolver( AuthenticationTrustResolver trustResolver) {
  97. this. trustResolver = trustResolver;
  98. }
  99. public void setRoleHierarchy( RoleHierarchy roleHierarchy) {
  100. this. roleHierarchy = roleHierarchy;
  101. }
  102. /**
  103. * <p>
  104. * Sets the default prefix to be added to {@link #hasAnyRole(String...)} or
  105. * {@link #hasRole(String)}. For example, if hasRole("ADMIN") or hasRole("ROLE_ADMIN")
  106. * is passed in, then the role ROLE_ADMIN will be used when the defaultRolePrefix is
  107. * "ROLE_" (default).
  108. * </p>
  109. *
  110. * <p>
  111. * If null or empty, then no default role prefix is used.
  112. * </p>
  113. *
  114. * @param defaultRolePrefix the default prefix to add to roles. Default "ROLE_".
  115. */
  116. public void setDefaultRolePrefix( String defaultRolePrefix) {
  117. this. defaultRolePrefix = defaultRolePrefix;
  118. }
  119. private Set< String> getAuthoritySet( ) {
  120. if (roles == null) {
  121. roles = new HashSet< String>();
  122. Collection<? extends GrantedAuthority> userAuthorities = authentication
  123. . getAuthorities();
  124. if (roleHierarchy != null) {
  125. userAuthorities = roleHierarchy
  126. . getReachableGrantedAuthorities(userAuthorities);
  127. }
  128. roles = AuthorityUtils. authorityListToSet(userAuthorities);
  129. }
  130. return roles;
  131. }
  132. public boolean hasPermission( Object target, Object permission) {
  133. return permissionEvaluator. hasPermission(authentication, target, permission);
  134. }
  135. public boolean hasPermission( Object targetId, String targetType, Object permission) {
  136. return permissionEvaluator. hasPermission(authentication, ( Serializable) targetId,
  137. targetType, permission);
  138. }
  139. public void setPermissionEvaluator( PermissionEvaluator permissionEvaluator) {
  140. this. permissionEvaluator = permissionEvaluator;
  141. }
  142. /**
  143. * Prefixes role with defaultRolePrefix if defaultRolePrefix is non-null and if role
  144. * does not already start with defaultRolePrefix.
  145. *
  146. * @param defaultRolePrefix
  147. * @param role
  148. * @return
  149. */
  150. private static String getRoleWithDefaultPrefix( String defaultRolePrefix, String role) {
  151. if (role == null) {
  152. return role;
  153. }
  154. if (defaultRolePrefix == null || defaultRolePrefix. length() == 0) {
  155. return role;
  156. }
  157. if (role. startsWith(defaultRolePrefix)) {
  158. return role;
  159. }
  160. return defaultRolePrefix + role;
  161. }
  162. }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194

通过阅读源码,我们可以更加深刻的理解其EL写法,并在写代码的时候正确的使用。变量defaultRolePrefix硬编码约定了role的前缀是"ROLE_"。

同时,我们可以看出hasRole跟hasAnyRole是一样的。hasAnyRole是调用的hasAnyAuthorityName(defaultRolePrefix, roles)。所以,我们在学习一个框架或者一门技术的时候,最准确的就是源码。通过源码,我们可以更好更深入的理解技术的本质。

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

表达式描述
hasRole([role])当前用户是否拥有指定角色。
hasAnyRole([role1,role2])多个角色是一个以逗号进行分隔的字符串。如果当前用户拥有指定角色中的任意一个则返回true。
hasAuthority([auth])等同于hasRole
hasAnyAuthority([auth1,auth2])等同于hasAnyRole
Principle代表当前用户的principle对象
authentication直接从SecurityContext获取的当前Authentication对象
permitAll总是返回true,表示允许所有的
denyAll总是返回false,表示拒绝所有的
isAnonymous()当前用户是否是一个匿名用户
isRememberMe()表示当前用户是否是通过Remember-Me自动登录的
isAuthenticated()表示当前用户是否已经登录认证成功了。
isFullyAuthenticated()如果当前用户既不是一个匿名用户,同时又不是通过Remember-Me自动登录的,则返回true。

比如说,在lightsword系统中,我们设置测试报告页面,只针对ADMIN权限开放,代码如下:


   
   
  1. package com.springboot. in.action.controller
  2. import java.util
  3. import com.alibaba.fastjson.serializer.SerializerFeature
  4. import com.springboot. in.action.dao.HttpReportDao
  5. import org.springframework.beans.factory. annotation.Autowired
  6. import org.springframework.security.access.prepost.PreAuthorize
  7. import org.springframework.ui.Model
  8. import org.springframework.web.bind. annotation.{RequestMapping, RestController}
  9. import org.springframework.web.servlet.ModelAndView
  10. import scala.collection.JavaConversions._
  11. @RestController
  12. @RequestMapping(Array("/httpreport"))
  13. class HttpReportController @Autowired()(val HttpReportDao: HttpReportDao) {
  14. @RequestMapping(value = {
  15. Array("", "/")
  16. })
  17. @PreAuthorize("hasRole('ADMIN')") // Spring Security默认的角色前缀是”ROLE_”,使用hasRole方法时已经默认加上了
  18. def list(model: Model) = {
  19. val reports = HttpReportDao.findAll
  20. model.addAttribute( "reports", reports)
  21. val rateList = new util.ArrayList[ Double]
  22. val trendList = new util.ArrayList[Object]
  23. for (r <- reports) {
  24. rateList.add(r.rate)
  25. // QualityTrend
  26. val qt = new util.HashMap[String, Any]
  27. qt.put( "id", r.id)
  28. qt.put( "failed", r.fail)
  29. qt.put( "totalCases", r.pass + r.fail)
  30. qt.put( "rate", r.rate)
  31. trendList.add(qt)
  32. }
  33. val jsonstr = com.alibaba.fastjson.JSON.toJSONString(trendList, SerializerFeature.BrowserCompatible)
  34. println(jsonstr)
  35. model.addAttribute( "rateList", rateList)
  36. model.addAttribute( "trendList", jsonstr)
  37. new ModelAndView( "/httpreport/list")
  38. }
  39. }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53

然后,我们配置用户user为USER权限:


   
   
  1. @Override
  2. protected void configure(AuthenticationManagerBuilder auth) throws Exception {
  3. auth
  4. .inMemoryAuthentication()
  5. .withUser( "root")
  6. . password( "root")
  7. . roles( "ADMIN", "USER")
  8. . and()
  9. .withUser( "admin"). password( "admin")
  10. . roles( "ADMIN", "USER")
  11. . and()
  12. .withUser( "user"). password( "user")
  13. . roles( "USER");
  14. }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

重启应用,用用户名:user,密码:user登录系统,访问/httpreport页面,我们将会看到如下,不允许访问的报错页面:

1240
简要说明

在方法上添加@PreAuthorize这个注解,value="hasRole('ADMIN')")是Spring-EL expression,当表达式值为true,标识这个方法可以被调用。如果表达式值是false,标识此方法无权限访问。

本小节完整的工程代码:
https://github.com/EasySpringBoot/lightsword/tree/spring_security_with_in_memory_auth

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

如果我们想要在前端页面显示当前登录的用户怎么办呢?在在Spring Security里面怎样获取当前登录认证通过的用户信息?下面我们就来探讨这个问题。

很好办。我们添加一个LoginFilter,默认拦截所有请求,把当前登录的用户放到系统session中即可。在Spring Security中,用户信息保存在SecurityContextHolder中。Spring Security使用一个Authentication对象来持有所有系统的安全认证相关的信息。这个信息的内容格式如下:


   
   
  1. {
  2. "accountNonExpired" :true ,
  3. "accountNonLocked" :true ,
  4. "authorities":[{
  5. "authority": "ROLE_ADMIN"
  6. } ,{
  7. "authority": "ROLE_USER"
  8. }] ,
  9. "credentialsNonExpired" :true ,
  10. "enabled" :true ,
  11. "username": "root"
  12. }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

这个Authentication对象信息其实就是User实体的信息(当然,密码没放进来)。


   
   
  1. public class User implements UserDetails, CredentialsContainer {
  2. private String password;
  3. private final String username;
  4. private final Set<GrantedAuthority> authorities;
  5. private final boolean accountNonExpired;
  6. private final boolean accountNonLocked;
  7. private final boolean credentialsNonExpired;
  8. private final boolean enabled;
  9. ....
  10. }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

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


   
   
  1. Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
  2. if (principal instanceof UserDetails) {
  3. String username = ((UserDetails)principal).getUsername();
  4. } else {
  5. String username = principal.toString();
  6. }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

通过调用getContext()返回的对象是SecurityContext的实例对象,该实例对象保存在ThreadLocal线程本地存储中。使用Spring Security框架,通常的认证机制都是返回UserDetails实例。

Spring MVC的 Web开发使用 Controller 基本上可以完成大部分需求,但是我们还可能会用到 Servlet、Filter、Listener、Interceptor 等等。

在Spring Boot中添加自己的Servlet有两种方法,代码注册Servlet和注解自动注册(Filter和Listener也是如此)。

(1)代码注册通过ServletRegistrationBean、 FilterRegistrationBean 和 ServletListenerRegistrationBean 获得控制。
也可以通过实现 ServletContextInitializer 接口直接注册。使用代码注册Servlet(就不需要@ServletComponentScan注解)

(2)在 SpringBootApplication 上使用@ServletComponentScan 注解后,Servlet、Filter、Listener 可以直接通过 @WebServlet、@WebFilter、@WebListener 注解自动注册。

下面我们就采用第(2)种方法,通过添加一个LoginFilter,拦截所有请求,把当前登录信息放到系统session中,并在前端页面显示。

1.添加一个实现了javax.servlet.Filter的LoginFilter,把当前登录信息放到系统session中

代码如下


   
   
  1. package com.springboot.in.action.filter
  2. import javax.servlet._
  3. import javax.servlet.annotation. WebFilter
  4. import javax.servlet.http. HttpServletRequest
  5. import com.alibaba.fastjson. JSON
  6. import com.alibaba.fastjson.serializer. SerializerFeature
  7. import org.springframework.core.annotation. Order
  8. /**
  9. * Created by jack on 2017/4/28.
  10. */
  11. @Order( 1) //@Order注解表示执行过滤顺序,值越小,越先执行
  12. @WebFilter(filterName = "loginFilter", urlPatterns = Array( "/*")) //需要在spring-boot的入口处加注解@ServletComponentScan, 如果不指定,默认url-pattern是/*
  13. class LoginFilter extends Filter {
  14. override def init(filterConfig: FilterConfig): Unit = {}
  15. override def destroy(): Unit = {}
  16. override def doFilter(request: ServletRequest, response: ServletResponse, chain: FilterChain): Unit = {
  17. val session = request.asInstanceOf[ HttpServletRequest].getSession
  18. import org.springframework.security.core.context. SecurityContextHolder
  19. import org.springframework.security.core.userdetails. UserDetails
  20. val principal = SecurityContextHolder.getContext.getAuthentication.getPrincipal
  21. println( "LoginFilter:" + JSON.toJSONString(principal, SerializerFeature. PrettyFormat))
  22. var username = ""
  23. if (principal.isInstanceOf[ UserDetails]) {
  24. username = principal.asInstanceOf[ UserDetails].getUsername
  25. }
  26. else {
  27. username = principal.toString
  28. }
  29. session.setAttribute( "username", username)
  30. chain.doFilter(request, response)
  31. }
  32. }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43

我们通过


   
   
  1. val principal = SecurityContextHolder.getContext.getAuthentication.getPrincipal
  2. if (principal.isInstanceOf[UserDetails]) {
  3. username = principal.asInstanceOf[UserDetails].getUsername
  4. }
  5. else {
  6. username = principal.toString
  7. }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

拿到认证信息,然后把用户名放到session中:


   
   
  1. session.setAttribute( "username", username)
  2. chain. do Filter( request, response)
  • 1
  • 2

其中,@WebFilter(filterName = "loginFilter", urlPatterns = Array("/")) ,这个注解用来声明一个Servlet的Filter,这个加注解@WebFilter的LoginFilter类必须要实现javax.servlet.Filter接口。它会在容器部署的时候扫描处理。如果不指定urlPatterns,默认url-pattern是/。这个@WebFilter注解,在SpringBoot中,要给启动类加上注解@ServletComponentScan,开启扫描Servlet组件功能。

2.给启动类加上注解@ServletComponentScan


   
   
  1. package com.springboot. in.action
  2. import org.springframework.boot.autoconfigure.SpringBootApplication
  3. import org.springframework.boot.web.servlet.ServletComponentScan
  4. @SpringBootApplication
  5. @ServletComponentScan(basePackages = Array("com.springboot.in.action"))
  6. class AppConfig
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

这个注解将开启扫描Servlet组件功能。那些被标注了@WebFilter,@WebServlet,@WebListener的Bean将会注册到容器中。需要注意的一点是,这个扫描动作只在当我们使用的是嵌入式Servlet容器的时候才起作用。完成Bean注册工作的类是org.springframework.boot.web.servlet.ServletComponentScanRegistrar,它实现了Spring的ImportBeanDefinitionRegistrar接口。

3.前端显示用户信息

Velocity内置了一些对象,例如:$request、$response、$session,这些对象可以在vm模版里可以直接调用。所以我们只需要使用$session取出,当初我们放进session的对应key的属性值即可。

我们在LoginFilter里面是这样放进去的:

session.setAttribute("username", username)
   
   
  • 1

在前端页面直接这样取出username


   
   
  1. <div class="pull-left info">
  2. <p>$session.getAttribute('username') </p>
  3. <a href="#"> <i class="fa fa-circle text-success"> </i> Online </a>
  4. </div>
  • 1
  • 2
  • 3
  • 4

4.运行测试

部署应用运行,我们看一下运行日志:


   
   
  1. 2017- 04- 28 21: 42: 46.072 INFO 2961 --- [ main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat initialized with port(s): 8888 (http)
  2. 2017- 04- 28 21: 42: 46.097 INFO 2961 --- [ main] o.apache.catalina.core.StandardService : Starting service Tomcat
  3. 2017- 04- 28 21: 42: 46.099 INFO 2961 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet Engine: Apache Tomcat/ 8.0. 33
  4. 2017- 04- 28 21: 42: 46.328 INFO 2961 --- [ost-startStop- 1] o.a.c.c.C.[Tomcat].[localhost].[ /] : Initializing Spring embedded WebApplicationContext
  5. 2017-04-28 21:42:46.328 INFO 2961 --- [ost-startStop-1] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 4325 ms
  6. 2017-04-28 21:42:46.984 INFO 2961 --- [ost-startStop-1] o.s.b.c.embedded.FilterRegistrationBean : Mapping filter: 'characterEncodingFilter' to: [/*]
  7. 2017- 04- 28 21: 42: 46.984 INFO 2961 --- [ost-startStop- 1] o.s.b.c.embedded.FilterRegistrationBean : Mapping filter: 'hiddenHttpMethodFilter' to: [ /*]
  8. 2017-04-28 21:42:46.985 INFO 2961 --- [ost-startStop-1] o.s.b.c.embedded.FilterRegistrationBean : Mapping filter: 'httpPutFormContentFilter' to: [/*]
  9. 2017- 04- 28 21: 42: 46.985 INFO 2961 --- [ost-startStop- 1] o.s.b.c.embedded.FilterRegistrationBean : Mapping filter: 'requestContextFilter' to: [ /*]
  10. 2017-04-28 21:42:46.987 INFO 2961 --- [ost-startStop-1] .e.DelegatingFilterProxyRegistrationBean : Mapping filter: 'springSecurityFilterChain' to: [/*]
  11. 2017- 04- 28 21: 42: 46.988 INFO 2961 --- [ost-startStop- 1] o.s.b.c.embedded.FilterRegistrationBean : Mapping filter: 'com.springboot.in.action.filter.LoginFilter' to: [ /*]
  12. 2017-04-28 21:42:46.988 INFO 2961 --- [ost-startStop-1] o.s.b.c.e.ServletRegistrationBean : Mapping servlet: 'dispatcherServlet' to [/]
  13. 2017- 04- 28 21: 42: 47.734 INFO 2961 --- [ost-startStop- 1] o.s.s.web.DefaultSecurityFilterChain : Creating filter chain: org.springframework.security.web.util.matcher.AnyRequestMatcher@1, [org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@7a20e3e2, org.springframework.security.web.context.SecurityContextPersistenceFilter@6d522c58, org.springframework.security.web.header.HeaderWriterFilter@43ba5fdb, org.springframework.security.web.csrf.CsrfFilter@4ae04f7a, org.springframework.security.web.authentication.logout.LogoutFilter@31e3441f, org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter@30dfa22c, org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter@605f9361, org.springframework.security.web.authentication.www.BasicAuthenticationFilter@22a03a5f, org.springframework.security.web.savedrequest.RequestCacheAwareFilter@7806751c, org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@67831f83, org.springframework.security.web.authentication.AnonymousAuthenticationFilter@134ae8c4, org.springframework.security.web.session.SessionManagementFilter@4c60d4b8, org.springframework.security.web.access.ExceptionTranslationFilter@2be01c38, org.springframework.security.web.access.intercept.FilterSecurityInterceptor@34cb7b6d]
  14. 2017- 04- 28 21: 42: 48.105 INFO 2961 --- [ main] j.LocalContainerEntityManagerFactoryBean : Building JPA container EntityManagerFactory for persistence unit 'default'
  15. 2017- 04- 28 21: 42: 48.121 INFO 2961 --- [ main] o.hibernate.jpa.internal.util.LogHelper : HHH000204: Processing PersistenceUnitInfo [
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

在上面的日志里面,我们可以看到如下一行

o.s.b.c.embedded.FilterRegistrationBean  : Mapping filter: 'com.springboot.in.action.filter.LoginFilter' to: [/*]
   
   
  • 1

这表明我们定义的LoginFilter类成功注册,路径映射到/*。同时,我们在

o.s.s.web.DefaultSecurityFilterChain     : Creating filter chain:
   
   
  • 1

这行日志后面,看到SpringBoot默认创建了的那些Filter Chain。这些Filter如下:


   
   
  1. org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter@7a20e3e2,
  2. org.springframework.security.web.context.SecurityContextPersistenceFilter@6d522c58,
  3. org.springframework.security.web.header.HeaderWriterFilter@43ba5fdb,
  4. org.springframework.security.web.csrf.CsrfFilter@4ae04f7a,
  5. org.springframework.security.web.authentication.logout.LogoutFilter@31e3441f,
  6. org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter@30dfa22c,
  7. org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter@605f9361,
  8. org.springframework.security.web.authentication.www.BasicAuthenticationFilter@22a03a5f,
  9. org.springframework.security.web.savedrequest.RequestCacheAwareFilter@7806751c,
  10. org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter@67831f83,
  11. org.springframework.security.web.authentication.AnonymousAuthenticationFilter@134ae8c4,
  12. org.springframework.security.web.session.SessionManagementFilter@4c60d4b8,
  13. org.springframework.security.web.access.ExceptionTranslationFilter@2be01c38,
  14. org.springframework.security.web.access.intercept.FilterSecurityInterceptor@34cb7b6d
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

SpringBoot在背后,为我们默默做了这么多事情。

好了,言归正传,我们使用root用户名登录,我们可以看到页面上正确展示了我们当前登录的用户,如下图

1240
SpringBoot注册Servlet、Filter、Listener的方法

我们刚才是使用@WebFilter注解一个javax.servlet.Filter的实现类来实现一个LoginFilter。

基于JavaConfig,SpringBoot同样可以使用如下的方式实现Servlet、Filter、Listener的Bean的配置:


   
   
  1. @Configuration
  2. public class WebConfig {
  3. @Bean
  4. public ServletRegistrationBean servletRegistrationBean_demo2( ){
  5. ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean();
  6. servletRegistrationBean. addUrlMappings( "/demo-servlet");
  7. servletRegistrationBean. setServlet( new DemoServlet());
  8. return servletRegistrationBean;
  9. }
  10. @Bean
  11. public FilterRegistrationBean filterRegistrationBean( ){
  12. FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
  13. filterRegistrationBean. setFilter( new LoginFilter());
  14. Set< String> set = new HashSet< String>();
  15. set. add( "/*");
  16. filterRegistrationBean. setUrlPatterns(set);
  17. return filterRegistrationBean;
  18. }
  19. @Bean
  20. public ServletListenerRegistrationBean servletListenerRegistrationBean( ){
  21. ServletListenerRegistrationBean servletListenerRegistrationBean = new ServletListenerRegistrationBean();
  22. servletListenerRegistrationBean. setListener( new Log4jConfigListener());
  23. servletListenerRegistrationBean. addInitParameter( "log4jConfigLocation", "classpath:log4j.properties");
  24. return servletListenerRegistrationBean;
  25. }
  26. }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

从这里我们可以看出,JavaConfig在SpringBoot的自动配置中实现Bean注册的基本使用方式。

进阶 Security: 用数据库存储用户和角色,实现安全认证

本节我们将在我们之前的系统上,实现一个用数据库存储用户和角色,实现系统的安全认证。在权限角色上,我们简单设计两个用户角色:USER,ADMIN。

我们设计页面的权限如下:

首页/ : 所有人可访问
登录页 /login: 所有人可访问
普通用户权限页 /httpapi, /httpsuite: 登录后的用户都可访问
管理员权限页 /httpreport : 仅管理员可访问
无权限提醒页: 当一个用户访问了其没有权限的页面,我们使用全局统一的异常处理页面提示。

1.数据库层设计:新建三张表User,Role,UserRole

对应的领域实体模型类如下:

用户表


   
   
  1. package com .springboot .in .action .entity
  2. import javax .persistence.{ Entity, GeneratedValue, GenerationType, Id}
  3. import scala .beans .BeanProperty
  4. /**
  5. * Created by jack on 2017/4/29.
  6. */
  7. @ Entity
  8. class User {
  9. @Id
  10. @GeneratedValue(strategy = GenerationType.AUTO)
  11. @BeanProperty
  12. var id: Integer = _
  13. @BeanProperty
  14. var userName: String = _
  15. @BeanProperty
  16. var password: String = _
  17. }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

角色表


   
   
  1. package com .springboot .in .action .entity
  2. import javax .persistence.{ Entity, GeneratedValue, GenerationType, Id}
  3. import scala .beans .BeanProperty
  4. /**
  5. * Created by jack on 2017/4/29.
  6. */
  7. @ Entity
  8. class Role {
  9. @Id
  10. @GeneratedValue(strategy = GenerationType.AUTO)
  11. @BeanProperty
  12. var id: Integer = _
  13. @BeanProperty
  14. var role: String = _
  15. }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

用户角色关联表


   
   
  1. package com .springboot .in .action .entity
  2. import javax .persistence.{ Entity, GeneratedValue, GenerationType, Id}
  3. import scala .beans .BeanProperty
  4. /**
  5. * Created by jack on 2017/4/29.
  6. */
  7. @ Entity
  8. class UserRole {
  9. @Id
  10. @GeneratedValue(strategy = GenerationType.AUTO)
  11. @BeanProperty
  12. var id: Integer = _
  13. @BeanProperty
  14. var userId: Integer = _
  15. @BeanProperty
  16. var roleId: Integer = _
  17. }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

为了方便测试,我们后面会写一个用户测试数据的自动生成的Bean,用来做测试数据的自动初始化工作。

2.配置Spring Security

我们首先使用Spring Security帮我们做登录、登出的处理,以及当用户未登录时只能访问: http://localhost:8888/ 以及 http://localhost:8888/login 两个页面。

同样的,我们要写一个继承WebSecurityConfigurerAdapter的配置类:


   
   
  1. package com.springboot.in.action.security;
  2. import com.springboot.in.action.service. LightSwordUserDetailService;
  3. import org.springframework.context.annotation. Bean;
  4. import org.springframework.context.annotation. Configuration;
  5. import org.springframework.security.config.annotation.authentication.builders. AuthenticationManagerBuilder;
  6. import org.springframework.security.config.annotation.method.configuration. EnableGlobalMethodSecurity;
  7. import org.springframework.security.config.annotation.web.builders. HttpSecurity;
  8. import org.springframework.security.config.annotation.web.configuration. EnableWebSecurity;
  9. import org.springframework.security.config.annotation.web.configuration. WebSecurityConfigurerAdapter;
  10. import org.springframework.security.core.userdetails. UserDetailsService;
  11. /**
  12. * Created by jack on 2017/4/27.
  13. */
  14. @Configuration
  15. @EnableWebSecurity
  16. @EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true)
  17. //使用@EnableGlobalMethodSecurity(prePostEnabled = true)
  18. // 这个注解,可以开启security的注解,我们可以在需要控制权限的方法上面使用@PreAuthorize,@PreFilter这些注解。
  19. public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
  20. //@Autowired
  21. //LightSwordUserDetailService lightSwordUserDetailService;
  22. @Override
  23. @Bean
  24. public UserDetailsService userDetailsService() { //覆盖写userDetailsService方法 (1)
  25. return new LightSwordUserDetailService();
  26. }
  27. /**
  28. * If subclassed this will potentially override subclass configure(HttpSecurity)
  29. *
  30. * @param http
  31. * @throws Exception
  32. */
  33. @Override
  34. protected void configure( HttpSecurity http) throws Exception {
  35. //super.configure(http);
  36. http.csrf().disable();
  37. http.authorizeRequests()
  38. .antMatchers( "/").permitAll()
  39. .antMatchers( "/amchart/**",
  40. "/bootstrap/**",
  41. "/build/**",
  42. "/css/**",
  43. "/dist/**",
  44. "/documentation/**",
  45. "/fonts/**",
  46. "/js/**",
  47. "/pages/**",
  48. "/plugins/**"
  49. ).permitAll() //默认不拦截静态资源的url pattern (2)
  50. .anyRequest().authenticated().and()
  51. .formLogin().loginPage( "/login") // 登录url请求路径 (3)
  52. .defaultSuccessUrl( "/httpapi").permitAll().and() // 登录成功跳转路径url(4)
  53. .logout().permitAll();
  54. http.logout().logoutSuccessUrl( "/"); // 退出默认跳转页面 (5)
  55. }
  56. @Override
  57. protected void configure( AuthenticationManagerBuilder auth) throws Exception {
  58. //auth
  59. // .inMemoryAuthentication()
  60. // .withUser("root")
  61. // .password("root")
  62. // .roles("ADMIN", "USER")
  63. // .and()
  64. // .withUser("admin").password("admin")
  65. // .roles("ADMIN", "USER")
  66. // .and()
  67. // .withUser("user").password("user")
  68. // .roles("USER");
  69. //AuthenticationManager使用我们的 lightSwordUserDetailService 来获取用户信息
  70. auth.userDetailsService(userDetailsService()); // (6)
  71. }
  72. }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85

这里只做了基本的配置,其中:

(1)覆盖写userDetailsService方法,具体的LightSwordUserDetailService实现类,我们下面紧接着会讲。

(2)默认不拦截静态资源的url pattern。我们也可以用下面的WebSecurity这个方式跳过静态资源的认证


   
   
  1. public void configure (WebSecurity web) throws Exception {
  2. web
  3. .ignoring()
  4. .antMatchers( "/resourcesDir/**");
  5. }
  • 1
  • 2
  • 3
  • 4
  • 5

(3)跳转登录页面url请求路径为/login,我们需要定义一个Controller把路径映射到login.html。代码如下


   
   
  1. package com.springboot.in.action.security
  2. import org.springframework.context.annotation. Configuration
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值