- 在pom.xml中添加相关依赖:数据库使用MySql,所以添加MySql驱动,然后要添加Spring Security的支持。
<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>
- 在application.properties文件中配置相关信息(使用JPA进行数据库访问):
/*mysql配置*/
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
/*thymeleaf配置*/
spring.thymeleaf.cache=false
/*jpa配置*/
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
- 定义用户和角色
-
- 定义角色:角色实体类和表都很简单,就两个字段,一个id,一个name属性表示角色的名称,实体类如下:
@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;
}
}
-
- 定义用户:在定义用户的时候需要实现UserDetails接口,这样我们的用户实体即为Spring Security所使用的用户。定义好用户之后,我们还要配置用户和角色之间的多对多关系,正常情况下,角色和权限是两回事,所以我们还需要重写getAuthorities方法,将用户的角色和权限关联起来,代码如下:
@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;
}
}
-
- 运行Project就会在数据库中自动帮我们生成三张表,用户表、角色表和两者的关联表
- 创建传值对象:数据创建成功之后,在客户端请求网页的时候我们需要有一个实体类用来向客户端传递消息
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;
}
}
- 创建数据访问接口(配置JPA访问数据)
public interface SysUserRepository extends JpaRepository<SysUser, Long> {
SysUser findByUsername(String username);
}
- 自定义SpringSecurity用户认证:实现SpringSecurity内的UserDetailsService接口来完成自定义查询用户的逻辑
- 首先这里我们需要重写UserDetailsService接口,然后实现该接口中的loadUserByUsername方法,通过该方法查询到对应的用户,这里之所以要实现UserDetailsService接口,是因为在Spring Security中我们配置相关参数需要UserDetailsService类型的数据。
public class CustomUserService implements UserDetailsService {
@Autowired
SysUserRepository userRepository;
@Override
public UserDetails loadUserByUsername(String name) throws UsernameNotFoundException {
SysUser user = userRepository.findByUsername(name);
if (user == null) {
throw new UsernameNotFoundException("用户名不存在");
}
System.out.println("name:"+name);
System.out.println("username:"+user.getUsername()+";password:"+user.getPassword());
return user;
}
}
- 配置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(); //注销请求可直接访问
}
}
-
- 首先当我们要自定义Spring Security的时候我们需要继承自WebSecurityConfigurerAdapter来完成,相关配置重写对应 方法即可。
-
- 我们在这里注册CustomUserService的Bean,然后通过重写configure方法添加我们自定义的认证方式。
-
- 在configure(HttpSecurity http)方法中,我们设置了登录页面,而且登录页面任何人都可以访问,然后设置了登录失败地址,也设置了注销请求,注销请求也是任何人都可以访问的。
-
- permitAll表示该请求任何人都可以访问,.anyRequest().authenticated(),表示其他的请求都必须要有权限认证。
-
- 这里我们可以通过匹配器来匹配路径,比如antMatchers方法,假设我要管理员才可以访问admin文件夹下的内容,我可以这样来写:.antMatchers("/admin/").hasRole(“ROLE_ADMIN”),也可以设置admin文件夹下的文件可以有多个角色来访问,写法如下:.antMatchers("/admin/").hasAnyRole(“ROLE_ADMIN”,“ROLE_USER”)
-
- 可以通过hasIpAddress来指定某一个ip可以访问该资源,假设只允许访问ip为210.210.210.210的请求获取admin下的资源,写法如下.antMatchers("/admin/**").hasIpAddress(“210.210.210.210”)
-
- 更多的权限控制方式参看下表:
-
- 这里我们还可以做更多的配置,参考如下代码:
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();
- SpringMVC配置:配置一个简单的SpringBoot内的MVC控制器跳转,下面我们添加一个名叫MVCConfig配置类继承WebMvcConfigurerAdapter类,重写addViewControllers()方法添加路径访问。
@Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/login").setViewName("login");
}
}
- 创建登陆界面:在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页面
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里边的内容
<!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>
- 添加控制器
@Controller
public class HomeController {
@RequestMapping("/")
public String index(Model model) {
Msg msg = new Msg("测试标题", "测试内容", "额外信息,只对管理员显示");
model.addAttribute("msg", msg);
return "index";
}
}
-
访问接口进行测试
-
参考博客:https://blog.****.net/u012702547/article/details/54319508