在Spring Boot中使用Spring Security实现权限控制

 

Spring Boot框架咱们前面已经介绍了不少了,相信看了前面的博客的小伙伴对Spring Boot应该有一个大体的了解了吧,若是有小伙伴对Spring Boot尚不熟悉,能够先移步这里从SpringMVC到Spring Boot,老司机请略过。OK,那咱们今天要说的是Spring Boot中另一个比较重要的东西,那就是Spring Security,这是一个专门针对基于Spring的项目的安全框架,它主要是利用了咱们前文介绍过的的AOP(Spring基础配置)来实现的。之前在Spring框架中使用Spring Security须要咱们进行大量的XML配置,可是,Spring Boot在这里依然有惊喜带给咱们,咱们今天就一块儿来看看。 
毫无疑问,Spring Boot针对Spring Security也提供了自动配置的功能,这些默认的自动配置极大的简化了咱们的开发工做,咱们今天就来看看这个吧。php

建立Project并添加相关依赖

Project的建立和前文同样,惟一要注意的地方就是建立的时候添加的依赖不一样,以下图: 
这里写图片描述 
OK,建立成功以后添加相关依赖,数据库我这里使用MySql,因此添加MySql驱动,而后要添加Spring Security的支持,因此还要添加Spring Security的依赖,以下:css

<dependency> <groupId>org.thymeleaf.extras</groupId> <artifactId>thymeleaf-extras-springsecurity4</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.40</version> </dependency>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

配置application.properties

这个东东的配置仍是和咱们上文说到的是同样的,这里也没啥好说的,有问题的小伙伴翻看前文(初识在Spring Boot中使用JPA):html

spring.datasource.driver-class-name=com.mysql.jdbc.Driver spring.datasource.url=jdbc:mysql://localhost:3306/sang?useUnicode=true&characterEncoding=utf-8 spring.datasource.username=root spring.datasource.password=sang logging.level.org.springframework.security=info spring.thymeleaf.cache=false spring.jpa.hibernate.ddl-auto=update spring.jpa.show-sql=true
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

定义用户和角色

咱们这里使用JPA来定义用户和角色,用户和角色都存储在数据库中,咱们直接经过在数据库中查询而后来使用。java

定义角色

咱们的角色实体类和表都很简单,就两个字段,一个id,一个name属性表示角色的名称,实体类以下;mysql

@Entity public class SysRole { @Id @GeneratedValue private Long id; private String name; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } }

OK,简简单单就这两个属性。git

定义用户

咱们在定义用户的时候须要实现UserDetails接口,这样咱们的用户实体即为Spring Security所使用的用户,定义好用户以后,咱们还要配置用户和角色之间的多对多关系,正常状况下,角色和权限是两回事,因此咱们还须要重写getAuthorities方法,将用户的角色和权限关联起来,代码以下:github

@Entity public class SysUser implements UserDetails { @Id @GeneratedValue private Long id; private String username; private String password; @ManyToMany(cascade = {CascadeType.REFRESH},fetch = FetchType.EAGER) private List<SysRole> roles; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public void setUsername(String username) { this.username = username; } public void setPassword(String password) { this.password = password; } public List<SysRole> getRoles() { return roles; } public void setRoles(List<SysRole> roles) { this.roles = roles; } @Override public Collection<? extends GrantedAuthority> getAuthorities() { List<GrantedAuthority> auths = new ArrayList<>(); List<SysRole> roles = this.getRoles(); for (SysRole role : roles) { auths.add(new SimpleGrantedAuthority(role.getName())); } return auths; } @Override public String getPassword() { return this.password; } @Override public String getUsername() { return this.username; } @Override public boolean isAccountNonExpired() { return true; } @Override public boolean isAccountNonLocked() { return true; } @Override public boolean isCredentialsNonExpired() { return true; } @Override public boolean isEnabled() { return true; } } 

 

OK,通过上面两个步骤以后咱们的用户就和角色关联起来了,这个时候运行Project就会在数据库中自动帮咱们生成三张表,用户表、角色表和二者的关联表,以下: 
这里写图片描述spring

预设数据

咱们先在表中定义好几个角色和用户,方便咱们后边作测试用,OK,预设数据的话,那咱们执行以下几行数据插入代码:sql

insert into `sys_role`(`id`,`name`) values (1,'ROLE_ADMIN'),(2,'ROLE_USER'); insert into `sys_user`(`id`,`password`,`username`) values (1,'root','root'),(2,'sang','sang'); insert into `sys_user_roles`(`sys_user_id`,`roles_id`) values (1,1),(2,2); 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

咱们向数据库中插入两个用户两个角色,再将这两个用户两个角色关联起来便可。数据库

建立传值对象

数据建立成功以后,在客户端请求网页的时候咱们须要有一个实体类用来向客户端传递消息,OK,那咱们建立一个MSG对象:

public class Msg { private String title; private String content; private String extraInfo; public Msg() { } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } public String getExtraInfo() { return extraInfo; } public void setExtraInfo(String extraInfo) { this.extraInfo = extraInfo; } public Msg(String title, String content, String extraInfo) { this.title = title; this.content = content; this.extraInfo = extraInfo; } }

这就是一个普通的类,没什么好说的。

建立数据访问接口

public interface SysUserRepository extends JpaRepository<SysUser, Long> { SysUser findByUsername(String username); }

这个也是写了n多遍的东西了,不赘述,关于这里若是小伙伴有疑问能够参考这里(初识在Spring Boot中使用JPA)。须要注意的是这里只须要一个根据用户名查询出用户的方法便可,不须要经过用户名和密码去查询。

自定义UserDetailsService

自定义UserDetailsService,实现相应的接口,以下:

public class CustomUserService implements UserDetailsService { @Autowired SysUserRepository userRepository; @Override public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException { SysUser user = userRepository.findByUsername(s); if (user == null) { throw new UsernameNotFoundException("用户名不存在"); } System.out.println("s:"+s); System.out.println("username:"+user.getUsername()+";password:"+user.getPassword()); return user; } }

首先这里咱们须要重写UserDetailsService接口,而后实现该接口中的loadUserByUsername方法,经过该方法查询到对应的用户,这里之因此要实现UserDetailsService接口,是由于在Spring Security中咱们配置相关参数须要UserDetailsService类型的数据。

SpringMVC配置

@Configuration public class WebMvcConfig extends WebMvcConfigurerAdapter { @Override public void addViewControllers(ViewControllerRegistry registry) { registry.addViewController("/login").setViewName("login"); } }

当用户访问login时跳转到login.html页面。

配置Spring Security

@Configuration public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Bean UserDetailsService customUserService() { return new CustomUserService(); } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { auth.userDetailsService(customUserService()); } @Override protected void configure(HttpSecurity http) throws Exception { http.authorizeRequests() .anyRequest().authenticated() .and().formLogin().loginPage("/login").failureUrl("/login?error").permitAll().and() .logout().permitAll(); } }

OK ,关于这个配置我要多说两句:

1.首先当咱们要自定义Spring Security的时候咱们须要继承自WebSecurityConfigurerAdapter来完成,相关配置重写对应 方法便可。 
2.咱们在这里注册CustomUserService的Bean,而后经过重写configure方法添加咱们自定义的认证方式。 
3.在configure(HttpSecurity http)方法中,咱们设置了登陆页面,并且登陆页面任何人均可以访问,而后设置了登陆失败地址,也设置了注销请求,注销请求也是任何人均可以访问的。 
4.permitAll表示该请求任何人均可以访问,.anyRequest().authenticated(),表示其余的请求都必需要有权限认证。 
5.这里咱们能够经过匹配器来匹配路径,好比antMatchers方法,假设我要管理员才能够访问admin文件夹下的内容,我能够这样来写:.antMatchers("/admin/**").hasRole("ROLE_ADMIN"),也能够设置admin文件夹下的文件能够有多个角色来访问,写法以下:.antMatchers("/admin/**").hasAnyRole("ROLE_ADMIN","ROLE_USER") 
6.能够经过hasIpAddress来指定某一个ip能够访问该资源,假设只容许访问ip为210.210.210.210的请求获取admin下的资源,写法以下.antMatchers("/admin/**").hasIpAddress("210.210.210.210") 
7.更多的权限控制方式参看下表: 
这里写图片描述
8.这里咱们还能够作更多的配置,参考以下代码:

http.authorizeRequests() .anyRequest().authenticated() .and().formLogin().loginPage("/login") //设置默认登陆成功跳转页面 .defaultSuccessUrl("/index").failureUrl("/login?error").permitAll() .and() //开启cookie保存用户数据 .rememberMe() //设置cookie有效期 .tokenValiditySeconds(60 * 60 * 24 * 7) //设置cookie的私钥 .key("") .and() .logout() //默认注销行为为logout,能够经过下面的方式来修改 .logoutUrl("/custom-logout") //设置注销成功后跳转页面,默认是跳转到登陆页面 .logoutSuccessUrl("") .permitAll();

OK,这里算是核心了,多说两句。

建立登陆页面

在template文件夹中建立login.html页面,内容以下:

<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"/> <title>登陆</title> <link rel="stylesheet" th:href="@{css/bootstrap.min.css}"/> <link rel="stylesheet" th:href="@{css/signin.css}"/> <style type="text/css"> body { padding-top: 50px; } .starter-template { padding: 40px 15px; text-align: center; } </style> </head> <body> <nav class="navbar navbar-inverse navbar-fixed-top"> <div class="container"> <div class="navbar-header"> <a class="navbar-brand" href="#">Spring Security演示</a> </div> <div id="navbar" class="collapse navbar-collapse"> <ul class="nav navbar-nav"> <li><a th:href="@{/}">首页</a></li> <li><a th:href="@{http://www.baidu.com}">百度</a></li> </ul> </div> </div> </nav> <div class="container"> <div class="starter-template"> <p th:if="${param.logout}" class="bg-warning">已注销</p> <p th:if="${param.error}" class="bg-danger">有错误,请重试</p> <h2>使用帐号密码登陆</h2> <form class="form-signin" role="form" name="form" th:action="@{/login}" action="/login" method="post"> <div class="form-group"> <label for="username">帐号</label> <input type="text" class="form-control" name="username" value="" placeholder="帐号"/> </div> <div class="form-group"> <label for="password">密码</label> <input type="password" class="form-control" name="password" placeholder="密码"/> </div> <input type="submit" id="login" value="Login" class="btn btn-primary"/> </form> </div> </div> </body> </html>

这里就是一个普通的html页面,用到了thymeleaf模板引擎(thymeleaf能够参考这两篇文章使用Spring Boot开发Web项目/使用Spring Boot开发Web项目(二)之添加HTTPS支持),

建立登陆成功后跳转页面

<!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org" xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4"> <head> <meta charset="UTF-8"/> <title sec:authentication="name"></title> <link rel="stylesheet" th:href="@{css/bootstrap.min.css}"/> <style type="text/css"> body { padding-top: 50px; } .starter-template { padding: 40px 15px; text-align: center; } </style> </head> <body> <nav class="navbar navbar-inverse navbar-fixed-top"> <div class="container"> <div class="navbar-header"> <a class="navbar-brand" href="#">Spring Security演示</a> </div> <div id="navbar" class="collapse navbar-collapse"> <ul class="nav navbar-nav"> <li><a th:href="@{/}">首页</a></li> <li><a th:href="@{http://www.baidu.com}">百度</a></li> </ul> </div> </div> </nav> <div class="container"> <div class="starter-template"> <h1 th:text="${msg.title}"></h1> <p class="bg-primary" th:text="${msg.content}"></p> <div sec:authorize="hasRole('ROLE_ADMIN')"> <p class="bg-info" th:text="${msg.extraInfo}"></p> </div> <div sec:authorize="hasRole('ROLE_USER')"> <p class="bg-info">无更多显示信息</p> </div> <form th:action="@{/logout}" method="post"> <input type="submit" class="btn btn-primary" value="注销"/> </form> </div> </div> </body> </html>

这里有以下几个问题须要说明:

1.在html标签中咱们引入的Spring Security 
2.经过sec:authentication=”name”咱们能够获取当前用户名 
3.sec:authorize="hasRole('ROLE_ADMIN')表示当前用户角色为ROLE_ADMIN的话显示里边的内容 
4.sec:authorize="hasRole('ROLE_USER')表示当前用户角色为ROLE_USER的话显示该DIV里边的内容

添加控制器

@Controller public class HomeController { @RequestMapping("/") public String index(Model model) { Msg msg = new Msg("测试标题", "测试内容", "额外信息,只对管理员显示"); model.addAttribute("msg", msg); return "index"; } }

测试

首页以下:

访问http://localhost:8080/自动跳转到http://localhost:8080/login 
这里写图片描述

登陆出错

输入错误的帐号密码进行登陆,结果以下: 
这里写图片描述

管理员登陆

使用管理员账号密码登陆,结果以下: 
这里写图片描述

普通用户登陆

使用普通用户账号密码登陆,结果以下: 
这里写图片描述

注销

点击注销按钮,结果以下: 
这里写图片描述

OK,以上就是对Spring Security的一个简单介绍,是否是比本身经过过滤器、拦截器神马的来弄简单多了。

相关文章
相关标签/搜索