大家好,我是“追梦蜗牛”,大家可以在公众号后台回复 “Java资料”获得技能提升的资料,绝对是干货。
前言
(一). 什么是Spring Security?
(二). 相关配置
(三). Spring Security实战
上篇文章为大家讲述了 Spring Boot中 数据缓存的使用;本篇文章接着上篇内容继续为大家介绍SpringBoot中 实现权限控制。
在web应用开发中,安全无疑是十分重要的,选择Spring Security来保护web应用是一个非常好的选择。Spring Security 是spring项目之中的一个安全模块,可以非常方便与spring项目无缝集成。特别是在spring boot项目中加入spring security更是十分简单。本篇我们介绍spring security,以及spring security在web应用中的使用。
Spring Security,这是一种基于 Spring AOP 和 Servlet 过滤器的安全框架。它提供全面的安全性解决方案,同时在 Web 请求级和方法调用级处理身份确认和授权。
http.authorizeRequests() .anyRequest().authenticated() .and() .formLogin() .and()
//开启cookie保 用户数据
.rememberMe()
//设置cookie有效期
.tokenValiditySeconds(60 * 60 * 24 * 7)
//设置cookie的私钥
.key("")
.logout()
//默认注销行为为logout,可以通过下面的方式来修改
.logoutUrl("/custom-logout")
//设置注销成功后跳转页面,默认是跳转到登录页面
.logoutSuccessUrl("")
.loginPage("/login") .permitAll()
.antMatchers("/resources/**", "/signup", "/about")
.antMatchers("/admin/**")
.hasRole("ADMIN") .antMatchers("/db/**")
.access("hasRole('ADMIN') and hasRole('DBA')") .anyRequest()
.authenticated() .httpBasic();
http.authorizeRequests()方法有多个子节点,每个匹配器按其声明
的顺序进行考虑。上面列出了常用的基本配置,包括请求授权,登录页面路径,
成功后跳转等
- 1.http.authorizeRequests()方法有多个子节点,每个匹配器按其声明的顺序进行考虑。
- 2我们指定了任何用户都可以访问的多种URL模式。具体来说,如果URL以“/ resources /”开头,等于“/ signup”或等于“/ about”,则任何用户都可以访问请求。
- 3 任何以“/ admin /”开头的URL都将仅限于具有“ROLE_ADMIN”角色的用户。您会注意到,由于我们正在调用hasRole方法,因此我们不需要指定“ROLE_”前缀。
- 4 任何以“/ db /”开头的URL都要求用户同时拥有“ROLE_ADMIN”和“ROLE_DBA”。您会注意到,由于我们使用的是hasRole表达式,因此我们不需要指定“ROLE_”前缀。
- 5 任何尚未匹配的URL只需要对用户进行身份验证
#创建项目
#数据库配置
我们这里还是通过mysql数据库来实现,配置数据库依赖
<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.40</version> </dependency>
配置数据库 application.properties
spring.datasource.driver-class-name=com.mysql.jdbc.Driver spring.datasource.url=jdbc:mysql://localhost:3306/springbootsecurity?useUnicode=true&characterEncoding=utf-8 spring.datasource.username=root spring.datasource.password=root logging.level.org.springframework.security=info spring.thymeleaf.cache=false spring.jpa.hibernate.ddl-auto=update spring.jpa.show-sql=true spring.jackson.serialization.indent_output=true
#用户和角色
我们使用JPA来定义用户和角色
package org.cxzc.myyoung.springbootsecurity; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.authority.SimpleGrantedAuthority; import org.springframework.security.core.userdetails.UserDetails; import javax.persistence.*; import java.util.ArrayList; import java.util.Collection; import java.util.List; @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; } }
1,用户实体实现UserDetails接口,用户实体为spring security所使用的用户
2,配置用户和角色的多对多关系
3,重写 getAuthorities 方法,将用户的角色作为权限
#角色类
package org.cxzc.myyoung.springbootsecurity; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; @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; } }
1,ID为自动生成
2,name为角色名称
#数据结构及初始化
上面两个实体类创建的目的就是为了 生成需要的数据表,这时会生成三张表
用户表:SYS_USER
角色表:SYS_ROLE
关联表:SYS_USER_ROLES
针对上面的三张表,创建一些数据来验证,数据创建格式如下:
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,'chen','chen'); insert into `sys_user_roles`(`sys_user_id`,`roles_id`) values (1,1),(2,2);
创建两个用户,角色分别为 ROLE_ADMIN 和ROLE_USER
#传值对象
用来测试不同角色用户的数据展示。
package org.cxzc.myyoung.springbootsecurity; 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; } }
#访问数据
package org.cxzc.myyoung.springbootsecurity; import org.springframework.data.jpa.repository.JpaRepository; public interface SysUserRepository extends JpaRepository<SysUser , Long> { SysUser findByUsername(String username); }
这里的访问权限很简单,这里只有一个通过用户名称,查询用户的方法。
#自定义UserDetailService
package org.cxzc.myyoung.springbootsecurity; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails. UserDetailsService; import org.springframework.security.core.userdetails .UsernameNotFoundException; 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; } }
1,自定义 这里我们需要重写UserDetailsService接口
2,重写 loadUserByUsername 方法获得用户
3,实现重写接口中的loadUserByUsername方法,通过该方法查询到对应的用户,这里之所以要实现UserDetailsService接口,是因为在Spring Security中我们配置相关参数需要UserDetailsService类型的数据。
#Spring MVC配置
package org.cxzc.myyoung.springbootsecurity; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport; @Configuration public class WebMvcConfig extends WebMvcConfigurationSupport { @Override public void addViewControllers(ViewControllerRegistry registry) { registry.addViewController("/login").setViewName("login"); } }
1,注册访问/login 转向login.html页面
#Spring Security 配置
package org.cxzc.myyoung.springbootsecurity; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.builders.WebSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.core.userdetails.UserDetailsService; @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(); } }
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.更多的权限控制方式参看下表:
#创建登录界面
在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>
界面效果不多说,比较简单,就是一个简单登录界面,失败的提示等
#首页
在template文件夹中创建index.html页面
<!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里边的内容
#控制器
package org.cxzc.myyoung.springbootsecurity; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; @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,本篇内容到这里就完成了,如果小伙伴还有疑问,可以 关注 公众号 加群,我们一起进步
参考:
1. 《JavaEE开发的颠覆者 Spring Boot实战》
本案例下载地址:
https://github.com/ProceduralZC/itcxzc/tree/master/springbootsecurity
我的开源项目:
一点知识学院(Spring Boot开源项目)
程序职场 简介:。专注于 Spring Boot ,微服务,前端APP,副业赚钱,职场规划,运营管理 等,关注后回复 Java资料 ,领取为你精心准备的学习干货!
一个专门讲述 技术,赚钱,成长 的公号
一个执着的职场程序员
每天早八点为你准备精彩文章