spring boot 整合Spring Security
程序员文章站
2022-04-19 22:33:48
...
Spring 是一个非常流行和成功的 Java 应用开发框架。Spring Security 基于 Spring 框架,提供了一套 Web 应用安全性的完整解决方案。
实体类
package com.springsecutity.secutity.entity;
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;
}
}
package com.springsecutity.secutity.entity;
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;
/**
* @Author: shanggq
* @date: 2018/12/12
* UserDetails 将当前用户交给 spring Security 去管理
*/
@Entity
public class SysUser implements UserDetails {
@Id
@GeneratedValue
private Long id;
private String username;
private String password;
// CascadeType.PERSIST 用于级联保存设置
// FetchType.EAGER 在级联保存的时候需要首先获取数据
@ManyToMany(cascade = {CascadeType.PERSIST}, fetch = FetchType.EAGER)
private List<SysRole> 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 boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
@Override
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
@Override
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public List<SysRole> getRoles() {
return roles;
}
public void setRoles(List<SysRole> roles) {
this.roles = roles;
}
}
Msg用于传递数据,在页面进行数据访问显示
package com.springsecutity.secutity.entity;
/**
* 用于传递用户的信息
*/
public class Msg {
private String title;
private String content;
private String etraInfo;
public Msg(String title, String content, String etraInfo) {
this.title = title;
this.content = content;
this.etraInfo = etraInfo;
}
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 getEtraInfo() {
return etraInfo;
}
public void setEtraInfo(String etraInfo) {
this.etraInfo = etraInfo;
}
}
MySecutityConfig 配置 核心配置
package com.springsecutity.secutity.config;
import com.springsecutity.secutity.service.CustomUserService;
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.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class MySecutityConfig extends WebSecurityConfigurerAdapter {
@Bean
public UserDetailsService customUserService() {
return new CustomUserService();
}
/**
* 用于设置用户登录成功和失败的页面,设置cookie的有效时间,设置私密
*
* @param http
* @throws Exception
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
// http.formLogin() // 通过formLogin 方法定制登录操作
// .loginPage("/login") //定制登录页面的访问地址
// .defaultSuccessUrl(".index") // 指定登录成功之后转向的页面
// .failureUrl("/login?error") // 指定登录失败之后转向的页面
// .permitAll()
// .and()
// .rememberMe() // 用于开启cookie存储用户的信息
// .tokenValiditySeconds(60 * 60 * 24) // 设置cookie存储用户的时间
// .key("myKey") // 设置cookie中的私密
// .and()
// .logout() // 开启注销的行为
// .logoutUrl("/custom-logout") // 注销的时候的url路径
// .logoutSuccessUrl("/logout-success") // 注销成功之后跳转的页面
// .permitAll();
http.authorizeRequests()
.antMatchers("/css/**", "/js/**", "/images/**", "/webjars/**", "**/favicon.ico", "/index")
.permitAll()
.anyRequest().authenticated()
.and()
.rememberMe()
.tokenValiditySeconds(60 * 60)
.key("mykey")
.rememberMeParameter("remember-me")
.rememberMeCookieName("workspace")
.and()
.formLogin()
.loginPage("/login")
// .successHandler(new ForwardAuthenticationSuccessHandler("/"))
.defaultSuccessUrl("/")
// 会去访问/login/error 的请求路径 ,如果是默认的/login?error 的路径 会默认有param 的属性
.failureUrl("/login?error")
.permitAll()
.and()
.logout()
.permitAll();
// 用于关闭跨域
http.csrf().disable();
}
/**
* 用于授权
*
* @param auth
* @throws Exception
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(customUserService()).passwordEncoder(new MyPasswordEncoder());
}
}
@EnableGlobalMethodSecurity(prePostEnabled = true) 该配置用于下面的密码的配置,需要使用到该配置
设置webMvc的配置 : 该配置主要是用于设置当访问/login的进行统一的处理,该配置也可以设置在上面的配置中
package com.springsecutity.secutity.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
@Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/login").setViewName("login");
}
}
密码的配置:加该配置的原因是,在其余操作完成之后,进行用户的登录的时候,会发现后台出现密码为空的错误
package com.springsecutity.secutity.config;
import org.springframework.security.crypto.password.PasswordEncoder;
public class MyPasswordEncoder implements PasswordEncoder {
@Override
public String encode(CharSequence charSequence) {
return charSequence.toString();
}
@Override
public boolean matches(CharSequence charSequence, String s) {
return s.equals(charSequence.toString());
}
}
Controller层
package com.springsecutity.secutity.controller;
import com.springsecutity.secutity.entity.Msg;
import com.springsecutity.secutity.entity.error;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.HashMap;
@Controller
public class HomeController {
@RequestMapping("/")
public String index(Model model) {
Msg msg = new Msg("测试标题", "测试内容", "额外信息,只对管理员展示,注销请求");
model.addAttribute("msg", msg);
return "index";
}
@RequestMapping("/login/error")
public String error(Model model) {
error error = new error("登录失败,请您重新登录", "");
model.addAttribute("param", error);
return "login";
}
@PreAuthorize("hasRole('ROLE_USER')" )
@RequestMapping("/getName")
@ResponseBody
public String getName() {
System.out.println(".....");
return "asd";
}
}
service层
package com.springsecutity.secutity.service;
import com.springsecutity.secutity.dao.SysUserRepository;
import com.springsecutity.secutity.entity.SysUser;
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
private SysUserRepository sysUserRepository;
@Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
SysUser username = sysUserRepository.findByUsername(s);
if (username == null) {
throw new UsernameNotFoundException("用户查询失败 。。");
}
return username;
}
}
dao层,使用的hibernate进行数据的访问,如果使用mybatis,可直接将该接口修改为mybatis的接口
package com.springsecutity.secutity.dao;
import com.springsecutity.secutity.entity.SysUser;
import org.springframework.data.jpa.repository.JpaRepository;
public interface SysUserRepository extends JpaRepository<SysUser, Long> {
SysUser findByUsername(String username);
}
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}"/>
<style type="text/css">
body {
padding-top: 50px;
}
.starter-tempalte {
padding: 40px 15px;
text-align: center;
}
</style>
</head>
<body>
<nav class="navber navbar-inverse navbar-fixed-top ">
<div class="container">
<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>-->
</ul>
</div>
</nav>
<div class="starter-tempalte">
<p th:if="${param.logout}" class="bg-waring">已成功注销</p>
<p th:if="${param.error}" class="bg-waring">有错误,请重试</p>
<h2>使用账号密码登录</h2>
<form name="form" th:action="@{/login}" method="post">
<div class="form-group">
<label for="username">账号</label>
<input type="text" class="form-control" name="username" id="username" value="" placeholder="账号">
</div>
<div class="form-group">
<label for="password"></label>
<input type="password" class="form-control" name="password" id="password" placeholder="密码">
</div>
<div>
<!-- 是否记住我功能勾选框 -->
<input id="remember-me" name="remember-me" type="checkbox"/>
<label for="remember-me">一周内记住我</label>
</div>
<input type="submit" id="login" value="Login" class="btn btn-primary">
</form>
</div>
</body>
</html>
其中param这个参数,是Security底层里面的数据,在核心配置的时候,如果登录错误会跳转到login?error中,这个是请求的Security底层的方法,如果不想使用该功能,可将登录失败的地址修改成自己的请求地址即可
index页面
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4">
<!--xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity4" 表示的是spring security 的标签支持-->
<head>
<meta charset="UTF-8">
<!--sec:authentication="name" 获得当前用户名-->
<title sec:authentication="name"></title>
<link rel="stylesheet" th:href="@{css/bootstrap.min.css}"/>
<script type="text/javascript" th:src="@{js/jquery-1.8.3.js}"></script>
<style type="text/css">
body {
padding-top: 50px;
}
.starter-tempalte {
padding: 40px 15px;
text-align: center;
}
</style>
</head>
<body>
<nav class="navber navbar-inverse navbar-fixed-top ">
<div class="container">
<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>
</ul>
<ul class="nav navbar-nav">
<li><a id="getname">用户名称</a></li>
</ul>
</div>
</nav>
<div class="container">
<div class="starter-tempalte">
<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.etraInfo}"></p>
</div>
<div sec:authorize="hasRole('ROLE_USER')">
<p class="bg-info" id="userRole">无更多信息</p>
</div>
<form th:action="@{/logout}" method="post">
<input type="submit" class="btn btn-primary" value="注销">
</form>
</div>
</div>
</body>
<script type="text/javascript">
$("#getname").on("click", function () {
$.get("http://localhost:5000/getName",function (data) {
if(data == null && data == ""){
$("#userRole").text("无更多信息");
}else{
$("#userRole").text(data);
}
});
});
</script>
</html>
该页面中测试了权限设置,在controller中添加了getName的方法,并在上面添加了权限限制,但是对于没有权限的用户的提示方法我没有思路,有解决办法的大佬希望能给我指点一下
sql
INSERT INTO SYS_USER (ID, username, PASSWORD) VALUES (1, 'wyf', 'wyf');
INSERT INTO SYS_USER (ID, username, PASSWORD) VALUES (2, 'wisely', 'wisely');
INSERT INTO SYS_ROLE (ID, name) VALUES (1, 'ROLE_ADMIN');
INSERT INTO SYS_ROLE (ID, name) VALUES (2, 'ROLE_USER');
INSERT INTO SYS_USER_ROLES (SYS_USER_ID, ROLES_ID) VALUES (1, 1);
INSERT INTO SYS_USER_ROLES (SYS_USER_ID, ROLES_ID) VALUES (2, 2);
pom.xml 使用到的依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-thymeleaf -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
<version>2.1.1.RELEASE</version>
</dependency>
<!-- thymeleaf 支持Security -->
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity4</artifactId>
<version>3.0.4.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-data-jpa -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<version>2.1.1.RELEASE</version>
</dependency>
application.yml
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/security?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&serverTimezone=UTC
username: root
password: root
thymeleaf:
cache: false
jpa:
show-sql: true
hibernate:
ddl-auto: update
logging:
level:
org:
springframework:
security: INFO
server:
port: 5000
本案例借鉴 《javaEE开发颠覆者》
推荐阅读
-
JSP 开发之Spring Boot 动态创建Bean
-
Spring Boot打war包的实例教程
-
spring boot 添加admin监控的方法
-
SpringBoot与spring security的结合的示例
-
spring boot thymeleaf 图片上传web项目根目录操作步骤
-
Spring boot通过HttpSessionListener监听器统计在线人数的实现代码
-
Spring boot 总结之跨域处理cors的方法
-
Spring boot 和Vue开发中CORS跨域问题解决
-
深入浅析Spring Security5中默认密码编码器
-
Spring boot怎么整合Mybatis