本文共 7095 字,大约阅读时间需要 23 分钟。
使用 Security进行权限验证 由发表在
Security,这是一种基于Spring AOP和Servlet过滤器的安全框架。它提供全面的安全性解决方案,同时在Web请求级和方法调用级处理身份确认和授权。在Spring Framework基础上,Spring Security充分利用了依赖注入(DI,Dependency Injection)和面向切面技术。今天我们就来学习一下如何利用Spring security方便的进行权限验证。
首先还是先定义我们所需要的依赖:
org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-security org.springframework.boot spring-boot-starter-thymeleaf org.springframework.boot spring-boot-starter-data-jpa org.hsqldb hsqldb runtime
在本次学习中,我们仍然使用作为框架,同时引入我们的核心学习库:spring-boot-starter-security
。模板引擎仍然选用我们熟悉的thymeleaf。最后使用了Spring Data Jpa+ hsqldb作为数据库。
本次学习,我们将实现一个简单的有权限验证的系统,在权限验证上,我们还可以加上一个简单的用户角色判断的验证,这样,我们就有以下几个页面:
页面确定后,我们来看看用户表需要哪些字段。为了简化处理,我们的用户表只需要用户名、密码以及一个角色字段来确定其身份即可。
这样,我们的简单系统设计就出来了,为了加深印象,我们先不管权限验证相关代码,我们先来实现整个简单的系统吧。
首先我们需要一个数据层以及业务层为我们的业务逻辑提供支持,由于我们没有业务逻辑,业务逻辑层就省去了,只需要利用来实现一个简单数据访问层即可。
首先来定义一个User对象,设置用户名、密码、角色三个字段:
@Entitypublic class User { @Enumerated(EnumType.STRING) private ROLE role; @Id private String username; private String password; ...}
然后定义UserRepository:
public interface UserRepository extends JpaRepository{ User findByUsername(String username);}
这样,数据访问层就实现好了。
在展现层,我们需要提供中的所有页面。Controller
代码如下:
@Controllerpublic class HomeController { @RequestMapping(value = {"", "/home"}, method=RequestMethod.GET) public String home(){ return "home"; } @RequestMapping(value = "/helloadmin", method=RequestMethod.GET) public String helloAdmin(){ return "helloAdmin"; } @RequestMapping(value = "/hellouser", method=RequestMethod.GET) public String helloUser(){ return "helloUser"; } @RequestMapping(value = "/login", method=RequestMethod.GET) public String login(){ return "login"; } @RequestMapping("/403") public String forbidden(){ return "403"; }}
注:其中返回的页面在src/main/resources/templates
下,都是最简单的HTML页面,这里不在赘述。
大家可以看到,最后一个403页面实际上应该是当用户访问了没有权限的页面后显示的页面,因此在这里我们需要设置:当发现请求的返回码是403
时,需要交给'/403'进行处理。
@Configurationpublic class ErrorPageConfig { @Bean public EmbeddedServletContainerCustomizer embeddedServletContainerCustomizer(){ return new MyCustomizer(); } private static class MyCustomizer implements EmbeddedServletContainerCustomizer { @Override public void customize(ConfigurableEmbeddedServletContainer container) { container.addErrorPages(new ErrorPage(HttpStatus.FORBIDDEN, "/403")); } }}
这里我们利用了Spring自带的EmbeddedServletContainerCustomizer进行设置。当Spring发现有类型为EmbeddedServletContainerCustomizer注册进来,便会调用EmbeddedServletContainerCustomizer的customize方法,此时,我们可以对整个Container进行设置,这里,我们添加了对于返回值为HttpStatus.FORBIDDEN
的请求,将其交给/403
进行处理。
当然,我们还需要添加一个系统的启动函数,这个大家都应该很熟悉了,没有用过Spring Boot的同学可以参考教材。
@SpringBootApplicationpublic class App { public static void main( String[] args ){ SpringApplication.run(App.class, args); }}
最后,让我们准备一些测试数据:
@Servicepublic class DataInit { @Autowired UserRepository userRepository; @PostConstruct public void dataInit(){ User admin = new User(); admin.setPassword("admin"); admin.setUsername("admin"); admin.setRole(User.ROLE.admin); userRepository.save(admin); User user = new User(); user.setPassword("user"); user.setUsername("user"); user.setRole(User.ROLE.user); userRepository.save(user); }}
现在,一切准备就绪,进入根目录,运行mvn spring-boot:run
,访问以下网页即可:
上节内容我们搭建了一个简单的网页应用,但是其中完全没有任何登录、登出、权限验证等等的处理,只能保证每个页面的简单访问。接下来我们就来用Spring Security添加这些功能。
我们首先使用Spring Security帮我们做登录、登出的处理,以及当用户未登录时只能访问以及
两个页面。
从数据库中获取用户信息的操作是必不可少的,我们首先来实现UserDetailsService,这个接口需要我们实现一个方法:loadUserByUsername
。即通过用户名加载与该用户的用户名、密码以及权限相关的信息。
public class CustomUserDetailsService implements UserDetailsService { @Autowired UserRepository userRepository; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { User user = userRepository.findByUsername(username); if(user == null){ throw new UsernameNotFoundException("not found"); } Listauthorities = new ArrayList<>(); authorities.add(new SimpleGrantedAuthority(user.getRole().name())); System.err.println("username is " + username + ", " + user.getRole().name()); return new org.springframework.security.core.userdetails.User(user.getUsername(), user.getPassword(), authorities); }}
然后我们添加一个SecurityConfig类来对Security进行配置,使AuthenticationManager
使用我们的CustomUserDetailsService
来获取用户信息:
@Configuration@EnableWebMvcSecurity@EnableGlobalMethodSecurity(prePostEnabled = true)public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override @Bean public UserDetailsService userDetailsService() { return new CustomUserDetailsService(); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(userDetailsService()); } @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .antMatchers("/", "/home").permitAll() .anyRequest().authenticated() .and() .formLogin() .loginPage("/login") .defaultSuccessUrl("/helloadmin") .permitAll() .and() .logout() .permitAll(); }}
我们可以看到,SecurityConfig
上添加了@EnableWebMvcSecurity
标注,使得Spring Security提供并且支持了Spring MVC的集成。
同时,SecurityConfig
还继承了WebSecurityConfigurerAdapter
类,并覆盖了其中的几个方法:
CustomUserDetailsService
实例化并添加进security的上下文当中CustomUserDetailsService
作为其UserDetailsService
实例。/helloadmin
页面,并且登录、登出页面都是不需要登录就可以访问的。 这时候重启我们的应用,再次访问,你会发现网页会自动跳转到登录页面,同时,我们的登录、登出功能已经实现好了。
实现一个UserDetailsService
,再对Spring Security进行配置,这样,登录、登出以及登录的验证功能就可以实现啦,怎么样,是不是特别的简单。
之前系统设计时,我们将用户分成了管理员与普通用户,那么,如何将这两类用户访问的页面分开,使得普通用户看不见管理员相关的页面呢?很简单,我们可以通过两行标注来实现这个功能,修改HomeController
:
@RequestMapping(value = "/helloadmin", method=RequestMethod.GET)@PreAuthorize("hasAnyRole('admin')")public String helloAdmin(){ return "helloAdmin";}@RequestMapping(value = "/hellouser", method=RequestMethod.GET)@PreAuthorize("hasAnyRole('admin', 'user')")public String helloUser(){ return "helloUser";}
修改helloAdmin
和helloUser
方法,添加@PreAuthorize
标注,当我们访问这两个URL的时候,Spring Security会帮我们验证当前用户是否有权限访问该地址。
现在重新启动服务,用user账号登陆,访问'',怎么样,是不是看见403页面啦。添加角色控制,就是这么简单。
更多文章请访问