小言_互联网的博客

看完这一篇,你就对 Spring Security 略窥门径了 | 原力计划

417人阅读  评论(0)

作者 | BoCong-Deng

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

头图 | CSDN 下载自东方 IC

出品 | CSDN(ID:CSDNnews)

写在前面

开发Web应用,对页面的安全控制通常是必须的。比如:对于没有访问权限的用户需要转到登录表单页面。要实现访问控制的方法多种多样,可以通过Aop、拦截器实现,也可以通过框架实现,例如:Apache Shiro、Spring Security。我们这里要讲的Spring Security 就是一个Spring生态中关于安全方面的框架。它能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案。

默认认证用户名密码

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


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

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

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

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

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


   
  1. 1@ConfigurationProperties(
  2. 2    prefix =  "spring.security"
  3. 3)
  4. 4public class SecurityProperties {
  5. 5    public static final  int BASIC_AUTH_ORDER =  2147483642;
  6. 6    public static final  int IGNORED_ORDER =  -2147483648;
  7. 7    public static final  int DEFAULT_FILTER_ORDER =  -100;
  8. 8    private final SecurityProperties.Filter filter =  new SecurityProperties.Filter();
  9. 9    private SecurityProperties.User user =  new SecurityProperties.User();
  10. 10
  11. 11    public SecurityProperties() {
  12. 12    }
  13. 13
  14. 14    public SecurityProperties.User getUser() {
  15. 15         return this.user;
  16. 16    }
  17. 17
  18. 18    public SecurityProperties.Filter getFilter() {
  19. 19         return this.filter;
  20. 20    }
  21. 21
  22. 22    public static class User {
  23. 23        private String name =  "user";
  24. 24        private String password = UUID.randomUUID().toString();
  25. 25        private List<String> roles =  new ArrayList();
  26. 26        private boolean passwordGenerated =  true;
  27. 27
  28. 28        public User() {
  29. 29        }
  30. 30
  31. 31        public String getName() {
  32. 32             return this.name;
  33. 33        }
  34. 34
  35. 35        public void setName(String name) {
  36. 36            this.name = name;
  37. 37        }
  38. 38
  39. 39        public String getPassword() {
  40. 40             return this.password;
  41. 41        }
  42. 42
  43. 43        public void setPassword(String password) {
  44. 44             if (StringUtils.hasLength(password)) {
  45. 45                this.passwordGenerated =  false;
  46. 46                this.password = password;
  47. 47            }
  48. 48        }
  49. 49
  50. 50        public List<String> getRoles() {
  51. 51             return this.roles;
  52. 52        }
  53. 53
  54. 54        public void setRoles(List<String> roles) {
  55. 55            this.roles =  new ArrayList(roles);
  56. 56        }
  57. 57
  58. 58        public boolean isPasswordGenerated() {
  59. 59             return this.passwordGenerated;
  60. 60        }
  61. 61    }
  62. 62
  63. 63    public static class Filter {
  64. 64        private  int order =  -100;
  65. 65        private Set<DispatcherType> dispatcherTypes;
  66. 66
  67. 67        public Filter() {
  68. 68            this.dispatcherTypes =  new HashSet(Arrays.asList(DispatcherType.ASYNC, DispatcherType.ERROR, DispatcherType.REQUEST));
  69. 69        }
  70. 70
  71. 71        public  int getOrder() {
  72. 72             return this.order;
  73. 73        }
  74. 74
  75. 75        public void setOrder( int order) {
  76. 76            this.order = order;
  77. 77        }
  78. 78
  79. 79        public Set<DispatcherType> getDispatcherTypes() {
  80. 80             return this.dispatcherTypes;
  81. 81        }
  82. 82
  83. 83        public void setDispatcherTypes(Set<DispatcherType> dispatcherTypes) {
  84. 84            this.dispatcherTypes = dispatcherTypes;
  85. 85        }
  86. 86    }
  87. 87}

综上所述,security默认的用户名是user, 默认密码是应用启动的时候,通过UUID算法随机生成的,默认的role是"USER"。当然,如果我们想简单改一下这个用户名密码,可以在application.properties配置你的用户名密码,例如


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

内存用户名密码认证

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


   
  1. 1import org.springframework.context.annotation.Configuration;
  2. 2import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
  3. 3import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
  4. 4import org.springframework.security.config.annotation.web.builders.HttpSecurity;
  5. 5import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
  6. 6import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
  7. 7
  8. 8@Configuration
  9. 9@EnableWebSecurity
  10. 10@EnableGlobalMethodSecurity(prePostEnabled =  true, securedEnabled =  true, jsr250Enabled =  true)
  11. 11public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
  12. 12    @Override
  13. 13    protected void configure(HttpSecurity http) throws Exception {
  14. 14        super.configure(http);
  15. 15    }
  16. 16
  17. 17    @Override
  18. 18    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
  19. 19        auth.inMemoryAuthentication()
  20. 20                .passwordEncoder( new BCryptPasswordEncoder())
  21. 21                .withUser( "admin")
  22. 22                .password( new BCryptPasswordEncoder().encode( "1234567"))
  23. 23                .roles( "USER");
  24. 24    }
  25. 25}

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

  • Spring security 5.0中新增了多种加密方式,也改变了默认的密码格式。需要修改一下configure中的代码,我们要将前端传过来的密码进行某种方式加密,Spring Security 官方推荐的是使用bcrypt加密方式。inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder()),这相当于登陆时用BCrypt加密方式对用户密码进行处理。以前的".password("123")" 变成了 “.password(new BCryptPasswordEncoder().encode("123"))”,这相当于对内存中的密码进行Bcrypt编码加密。如果比对时一致,说明密码正确,才允许登陆。

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

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

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

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

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

  • configureGlobal(AuthenticationManagerBuilder auth) 方法,在内存中创建了一个用户,该用户的名称为root,密码为root,用户角色为USER。这个默认的登录页面是怎么冒出来的呢?是的,SpringBoot内置的,SpringBoot甚至给我们做好了一个极简的登录页面。这个登录页面是通过Filter实现的。具体的实现类是org.springframework.security.web.authentication.ui.DefaultLoginPageGeneratingFilter。同时,这个DefaultLoginPageGeneratingFilter也是SpringBoot的默认内置的Filter。

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

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


   
  1. 1@Override
  2. 2    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
  3. 3        auth.inMemoryAuthentication()
  4. 4                .passwordEncoder( new BCryptPasswordEncoder())
  5. 5                .withUser( "admin")
  6. 6                .password( new BCryptPasswordEncoder().encode( "1234567"))
  7. 7                .roles( "USER")
  8. 8                .and()
  9. 9                .withUser( "admin1")
  10. 10                .password( new BCryptPasswordEncoder().encode( "123"))
  11. 11                .roles( "ADMIN""USER");
  12. 12    }

角色权限控制

当我们的系统功能模块当需求发展到一定程度时,会不同的用户,不同角色使用我们的系统。这样就要求我们的系统可以做到,能够对不同的系统功能模块,开放给对应的拥有其访问权限的用户使用。Spring Security提供了Spring EL表达式,允许我们在定义URL路径访问(@RequestMapping)的方法上面添加注解,来控制访问权限。在标注访问权限时,根据对应的表达式返回结果,控制访问权限:


   
  1. 1 true,表示有权限
  2. 2fasle,表示无权限

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


   
  1. 1public abstract class SecurityExpressionRoot implements SecurityExpressionOperations {
  2. 2    protected final Authentication authentication;
  3. 3    private AuthenticationTrustResolver trustResolver;
  4. 4    private RoleHierarchy roleHierarchy;
  5. 5    private Set<String> roles;
  6. 6    private String defaultRolePrefix =  "ROLE_";
  7. 7    public final boolean permitAll =  true;
  8. 8    public final boolean denyAll =  false;
  9. 9    private PermissionEvaluator permissionEvaluator;
  10. 10    public final String read =  "read";
  11. 11    public final String write =  "write";
  12. 12    public final String create =  "create";
  13. 13    public final String  delete =  "delete";
  14. 14    public final String admin =  "administration";
  15. 15
  16. 16    public SecurityExpressionRoot(Authentication authentication) {
  17. 17         if (authentication == null) {
  18. 18            throw  new IllegalArgumentException( "Authentication object cannot be null");
  19. 19        }  else {
  20. 20            this.authentication = authentication;
  21. 21        }
  22. 22    }
  23. 23
  24. 24    public final boolean hasAuthority(String authority) {
  25. 25         return this.hasAnyAuthority(authority);
  26. 26    }
  27. 27
  28. 28    public final boolean hasAnyAuthority(String... authorities) {
  29. 29         return this.hasAnyAuthorityName((String)null, authorities);
  30. 30    }
  31. 31
  32. 32    public final boolean hasRole(String role) {
  33. 33         return this.hasAnyRole(role);
  34. 34    }
  35. 35
  36. 36    public final boolean hasAnyRole(String... roles) {
  37. 37         return this.hasAnyAuthorityName(this.defaultRolePrefix, roles);
  38. 38    }
  39. 39
  40. 40    private boolean hasAnyAuthorityName(String prefix, String... roles) {
  41. 41        Set<String> roleSet = this.getAuthoritySet();
  42. 42        String[] var4 = roles;
  43. 43         int var5 = roles.length;
  44. 44
  45. 45         for( int var6 =  0; var6 < var5; ++var6) {
  46. 46            String role = var4[var6];
  47. 47            String defaultedRole = getRoleWithDefaultPrefix(prefix, role);
  48. 48             if (roleSet.contains(defaultedRole)) {
  49. 49                 return  true;
  50. 50            }
  51. 51        }
  52. 52
  53. 53         return  false;
  54. 54    }
  55. 55
  56. 56    public final Authentication getAuthentication() {
  57. 57         return this.authentication;
  58. 58    }
  59. 59
  60. 60    public final boolean permitAll() {
  61. 61         return  true;
  62. 62    }
  63. 63
  64. 64    public final boolean denyAll() {
  65. 65         return  false;
  66. 66    }
  67. 67
  68. 68    public final boolean isAnonymous() {
  69. 69         return this.trustResolver.isAnonymous(this.authentication);
  70. 70    }
  71. 71
  72. 72    public final boolean isAuthenticated() {
  73. 73         return !this.isAnonymous();
  74. 74    }
  75. 75
  76. 76    public final boolean isRememberMe() {
  77. 77         return this.trustResolver.isRememberMe(this.authentication);
  78. 78    }
  79. 79
  80. 80    public final boolean isFullyAuthenticated() {
  81. 81         return !this.trustResolver.isAnonymous(this.authentication) && !this.trustResolver.isRememberMe(this.authentication);
  82. 82    }
  83. 83
  84. 84    public Object getPrincipal() {
  85. 85         return this.authentication.getPrincipal();
  86. 86    }
  87. 87
  88. 88    public void setTrustResolver(AuthenticationTrustResolver trustResolver) {
  89. 89        this.trustResolver = trustResolver;
  90. 90    }
  91. 91
  92. 92    public void setRoleHierarchy(RoleHierarchy roleHierarchy) {
  93. 93        this.roleHierarchy = roleHierarchy;
  94. 94    }
  95. 95
  96. 96    public void setDefaultRolePrefix(String defaultRolePrefix) {
  97. 97        this.defaultRolePrefix = defaultRolePrefix;
  98. 98    }
  99. 99
  100. 100    private Set<String> getAuthoritySet() {
  101. 101         if (this.roles == null) {
  102. 102            Collection<? extends GrantedAuthority> userAuthorities = this.authentication.getAuthorities();
  103. 103             if (this.roleHierarchy != null) {
  104. 104                userAuthorities = this.roleHierarchy.getReachableGrantedAuthorities(userAuthorities);
  105. 105            }
  106. 106
  107. 107            this.roles = AuthorityUtils.authorityListToSet(userAuthorities);
  108. 108        }
  109. 109
  110. 110         return this.roles;
  111. 111    }
  112. 112
  113. 113    public boolean hasPermission(Object target, Object permission) {
  114. 114         return this.permissionEvaluator.hasPermission(this.authentication, target, permission);
  115. 115    }
  116. 116
  117. 117    public boolean hasPermission(Object targetId, String targetType, Object permission) {
  118. 118         return this.permissionEvaluator.hasPermission(this.authentication, (Serializable)targetId, targetType, permission);
  119. 119    }
  120. 120
  121. 121    public void setPermissionEvaluator(PermissionEvaluator permissionEvaluator) {
  122. 122        this.permissionEvaluator = permissionEvaluator;
  123. 123    }
  124. 124
  125. 125    private static String getRoleWithDefaultPrefix(String defaultRolePrefix, String role) {
  126. 126         if (role == null) {
  127. 127             return role;
  128. 128        }  else  if (defaultRolePrefix != null && defaultRolePrefix.length() !=  0) {
  129. 129             return role.startsWith(defaultRolePrefix) ? role : defaultRolePrefix + role;
  130. 130        }  else {
  131. 131             return role;
  132. 132        }
  133. 133    }
  134. 134}

通过阅读源码,我们可以更加深刻的理解其EL写法,并在写代码的时候正确的使用。变量defaultRolePrefix硬编码约定了role的前缀是"ROLE_"。同时,我们可以看出hasRole跟hasAnyRole是一样的。hasAnyRole是调用的hasAnyAuthorityName(defaultRolePrefix, roles)。所以,我们在学习一个框架或者一门技术的时候,最准确的就是源码。通过源码,我们可以更好更深入的理解技术的本质。

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

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

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

如果我们想要在前端页面显示当前登录的用户怎么办呢?在在Spring Security里面怎样获取当前登录认证通过的用户信息?下面我们就来探讨这个问题。其实很好办。我们添加一个LoginFilter,默认拦截所有请求,把当前登录的用户放到系统session中即可。在Spring Security中,用户信息保存在SecurityContextHolder中。Spring Security使用一个Authentication对象来持有所有系统的安全认证相关的信息。这个信息的内容格式如下:


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

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


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

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


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

通过调用getContext()返回的对象是SecurityContext的实例对象,该实例对象保存在ThreadLocal线程本地存储中。使用Spring Security框架,通常的认证机制都是返回UserDetails实例,通过如上这种方式,我们就可以拿到认证登录的用户信息。

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

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

  • 首页/ : 所有人可访问

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

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

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

  • 无权限提醒页:当一个用户访问了其没有权限的页面,我们使用全局统一的异常处理页面提示。

配置Spring Security

我们首先使用Spring Security帮我们做登录、登出的处理,以及当用户未登录时只能访问: http://localhost:8080/ 以及 http://localhost:8080/login 两个页面。同样的,我们要写一个继承WebSecurityConfigurerAdapter的配置类:


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

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

  • 覆盖写userDetailsService方法,具体的AdminUserDetailsService实现类,就是之前说的获取用户信息的service层类。

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


   
  1. 1public void configure(WebSecurity web) throws Exception {
  2. 2    web
  3. 3        .ignoring()
  4. 4        .antMatchers( "/resourcesDir/**");
  5. 5}
  • 跳转登录页面url请求路径为/login,我们需要定义一个Controller把路径映射到login.html。

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

  • 退出后跳转到的url为/

  • 认证鉴权信息的Bean,采用我们自定义的从数据库中获取用户信息的AdminUserDetailService类。

我们同样使用@EnableGlobalMethodSecurity(prePostEnabled = true)这个注解,开启security的注解,这样我们可以在需要控制权限的方法上面使用@PreAuthorize,@PreFilter这些注解。

用户退出

我们在configure(HttpSecurity http)方法里面定义了任何权限都允许退出,当然SpringBoot集成Security的默认退出请求是/logout

1http.logout().logoutSuccessUrl("/"); // 退出默认跳转页面 (4)

配置错误处理页面

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


   
  1. 1import org.springframework.web.bind.annotation.{ControllerAdvice, ExceptionHandler}
  2. 2import org.springframework.web.context.request.WebRequest
  3. 3import org.springframework.web.servlet.ModelAndView
  4. 4
  5. 5 /**
  6. 6  * Created by jack on 2017/4/27.
  7. 7  */
  8. 8@ControllerAdvice
  9. 9class GlobalExceptionHandlerAdvice {
  10. 10  @ExceptionHandler(value = Exception.class) //表示捕捉到所有的异常,你也可以捕捉一个你自定义的异常
  11. 11    public ModelAndView exception(Exception exception, WebRequest request){
  12. 12        ModelAndView modelAndView =  new ModelAndView( "/error");
  13. 13        modelAndView.addObject( "errorMessage", exception.getMessage());
  14. 14        modelAndView.addObject( "stackTrace", exception.getStackTrace());
  15. 15         return modelAndView;
  16. 16    }
  17. 17}

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

原文链接:

https://blog.csdn.net/DBC_121/article/details/104740273

【End】

2020 AI 开发者万人大会将于6月26日通过线上直播形式,让开发者们一站式学习了解当下 AI 的前沿技术研究、核心技术与应用以及企业案例的实践经验,同时还可以在线参加精彩多样的开发者沙龙与编程项目。参与前瞻系列活动、在线直播互动,不仅可以与上万名开发者们一起交流,还有机会赢取直播专属好礼与技术大咖连麦

今日福利:评论区留言入选,都可获得价值299元的「2020 AI开发者万人大会」在线直播门票一张。  快来动动手指,写下你想说的话吧

推荐阅读 

三大运营商将解决新老用户套餐不同权问题;罗永浩与抖音合作;Git 2.26发布 | 极客头条

程序员为什么千万不要瞎努力?

5 亿微博数据疑泄露,Python 爬虫如何避免踩天坑?

Kubernetes这么火你还不知道是啥?赶紧来看看吧~

医疗保健、零售、金融、制造业……一文带你看懂大数据对工业领域的影响!

如何开发日均吸引100万活跃用户的DApp?先收藏再看!

你点的每一个在看,我认真当成了喜欢

点击阅读原文,查看详情


转载:https://blog.csdn.net/csdnnews/article/details/105085710
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场