springboot项目实战-基于JPA的Spring Boot Security操作
发布时间
阅读量:
阅读量
新建一个test17项目,具体步骤如下:
在pom.xml中配置Spring Security与JPA相关技术架构,默认场景下已集成MySQL等数据库服务。
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</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-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity5</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.3.7</version>
</dependency>
</dependencies>
(2)为了增强项目的开发多样性,在本次开发中采用了yaml格式的配置文件。具体来说,在application.yml中新增了项目相关配置项用于设定数据库连接参数。同时,在sys数据库方面提供了详细的配置信息如下:
server.port=8089
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimeZone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=mysql123
spring.jpa.hibernate.ddl-auto=update
spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect
spring.jpa.open-in-view=false
spring.main.allow-circular-references=true
(3)开始编写项目代码,新建Security的配置文件WebSecurityConfig.java:
package com.shrimpking;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
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;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
import javax.sql.DataSource;
/** * Created by IntelliJ IDEA.
* * @Author : Shrimpking
* @create 2023/11/19 20:52
*/
@Configuration
@EnableGlobalMethodSecurity(securedEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter
{
@Autowired
private UserDetailService userDetailsService;
@Autowired
private PersistentTokenRepository persistentTokenRepository;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception
{
auth.authenticationProvider(authenticationProvider());
}
@Override
protected void configure(HttpSecurity http) throws Exception
{
//关闭csrf
http.csrf().disable();
//自定义登录页面
http.formLogin().loginPage("/loginPage")
//登录访问路径,只需定义url即可
.loginProcessingUrl("/login")
//登录失败的路径
.failureUrl("/exception")
//
.defaultSuccessUrl("/index",true);
//
http.authorizeRequests()
//放行名单
.antMatchers("/loginPage","/hello","/exception","/*.jpg").permitAll()
//其他认证才可以访问
.anyRequest().authenticated();
//注销
http.logout().logoutUrl("/logout");
//记住我
http.rememberMe()
//token
.tokenRepository(persistentTokenRepository)
//token过期时间
.tokenValiditySeconds( 60 * 60 )
.userDetailsService(userDetailsService);
}
/** * 登录提示
* @return
*/
@Bean
public AuthenticationProvider authenticationProvider(){
DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
//显示用户找不到异常,默认不论用户名或密码哪个错误,都提示密码错误
provider.setHideUserNotFoundExceptions(false);
provider.setPasswordEncoder(passwordEncoder());
provider.setUserDetailsService(userDetailsService);
return provider;
}
/** * 密码加密器
* @return
*/
@Bean
public PasswordEncoder passwordEncoder(){
//return PasswordEncoderFactories.createDelegatingPasswordEncoder();
return NoOpPasswordEncoder.getInstance();
}
@Bean
public PersistentTokenRepository persistentTokenRepository(DataSource dataSource){
JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
jdbcTokenRepository.setDataSource(dataSource);
return jdbcTokenRepository;
}
}
(4)新建Web请求的UserControllerjava入口文件,并定义其访问的URL:
package com.shrimpking;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.access.annotation.Secured;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.util.WebUtils;
import javax.security.sasl.AuthenticationException;
import javax.servlet.http.HttpServletRequest;
/** * Created by IntelliJ IDEA.
* * @Author : Shrimpking
* @create 2023/11/19 21:09
*/
@Controller
@Slf4j
public class UserController
{
@ResponseBody
@GetMapping("/hello")
public String hello(){
return "hello";
}
/** * 登录页面
* @return
*/
@GetMapping("/loginPage")
public String login(){
return "login";
}
/** * 认证异常
* @param request
* @return
*/
@GetMapping("/exception")
public String error(HttpServletRequest request) throws AuthenticationException
{
AuthenticationException exception
= (AuthenticationException)WebUtils.getSessionAttribute(request, "SPRING_SECURITY_LAST_EXCEPTION");
if(exception != null){
throw exception;
}
return "redierct:/loginPage";
}
@GetMapping({"/index","/"})
public String index(){
return "index";
}
@ResponseBody
@GetMapping("/role/teacher")
@Secured({"ROLE_teacher","ROLE_admin"})
public String teacher(){
return "模拟获取教师数据";
}
@ResponseBody
@GetMapping("/role/admin")
@Secured({"ROLE_admin"})
public String admin(){
return "模拟获取管理员数据";
}
@ResponseBody
@GetMapping("/role/student")
@Secured({"ROLE_student","ROLE_admin"})
public String student(){
return "模拟获取学生数据";
}
}
(5)新建UserDao.java文件和AuthoritiesDao.java文件进行数据库的操作。
package com.shrimpking;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
/** * Created by IntelliJ IDEA.
* * @Author : Shrimpking
* @create 2023/11/19 21:39
*/
public interface AuthoritiesDao extends JpaRepository<Authorities,Integer>, JpaSpecificationExecutor<Authorities>
{
}
package com.shrimpking;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
/** * Created by IntelliJ IDEA.
* * @Author : Shrimpking
* @create 2023/11/19 21:20
*/
public interface UsersDao extends JpaRepository<Users,Integer>,JpaSpecificationExecutor<Users>
{
Users findByUsername(String username);
}
(6)新建数据库的表对应的实体类Authorities、PersistentLogins和Users。
package com.shrimpking;
import lombok.Data;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.ManyToMany;
import javax.persistence.Table;
import java.util.HashSet;
import java.util.Set;
/** * Created by IntelliJ IDEA.
* * @Author : Shrimpking
* @create 2023/11/19 21:26
*/
@Data
@Entity
@Table(name = "ao_authorities")
public class Authorities
{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String authority;
@ManyToMany(mappedBy = "authorities",cascade = CascadeType.ALL)
private Set<Users> users = new HashSet<>();
}
package com.shrimpking;
import lombok.Data;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import java.util.Date;
/** * Created by IntelliJ IDEA.
* * @Author : Shrimpking
* @create 2023/11/19 21:29
*/
@Data
@Entity
@Table(name = "ao_persistent_logins")
public class PersistentLogins
{
@Id
private String series;
private String username;
private String token;
@Column(name = "last_used")
private Date lastUsed;
}
package com.shrimpking;
import lombok.Data;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.Table;
import java.util.HashSet;
import java.util.Set;
/** * Created by IntelliJ IDEA.
* * @Author : Shrimpking
* @create 2023/11/19 21:22
*/
@Data
@Entity
@Table(name = "ao_users")
public class Users
{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String username;
private String password;
@ManyToMany(targetEntity = Authorities.class,cascade = CascadeType.ALL)
@JoinTable(name = "ao_users_authorities",
joinColumns = @JoinColumn(name = "users_id",referencedColumnName = "id"),
inverseJoinColumns = @JoinColumn(name = "authorities_id",referencedColumnName = "id"))
private Set<Authorities> authorities = new HashSet<>();
}
drop table if exists ao_user;
create table ao_user(
id bigint not null auto_increment primary key comment '主键id',
user_name varchar(50) not null unique comment '用户名',
password varchar(100) default null comment '密码',
age int default null comment '年龄'
) comment '用户表';
drop table if exists ao_users;
create table ao_users(
id int not null auto_increment primary key comment '主键id',
username varchar(100) not null comment '用户名',
password varchar(255) not null comment '密码'
) comment '用户表2';
insert into ao_users values(1,'admin','123456');
drop table if exists ao_authorities;
create table ao_authorities(
id int not null auto_increment primary key comment '主键id',
authority varchar(200) not null comment '授权'
) comment '权限表';
drop table if exists ao_users_authorities;
create table ao_users_authorities(
id int not null auto_increment primary key comment '主键id',
users_id int not null comment '用户id',
authorities_id int not null comment '权限id'
) comment '用户权限关系表';
drop table if exists ao_persistent_logins;
create table ao_persistent_logins(
series varchar(100) not null primary key comment '主键',
username varchar(100) default null comment '用户名',
token varchar(255) default null comment '令牌',
last_used datetime default CURRENT_TIMESTAMP comment '最近登录时间'
) comment '保持登录';
(7)设置项目的全局异常处理:
package com.shrimpking;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.servlet.ModelAndView;
import java.nio.file.AccessDeniedException;
/** * Created by IntelliJ IDEA.
* * @Author : Shrimpking
* @create 2023/11/19 21:41
*/
@ControllerAdvice
@Slf4j
public class GlobalExceptionHandler
{
@ExceptionHandler(RuntimeException.class)
public ModelAndView exception(Exception e){
log.info(e.toString());
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("error");
if(e instanceof BadCredentialsException){
//密码错误
modelAndView.addObject("msg","密码错误");
}else if( e instanceof AccessDeniedException){
//权限不足
modelAndView.addObject("msg",e.getMessage());
}else {
modelAndView.addObject("msg","系统错误");
}
return modelAndView;
}
}
(8)设置用户的服务类,代码如下:
package com.shrimpking;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.Set;
/** * Created by IntelliJ IDEA.
* * @Author : Shrimpking
* @create 2023/11/19 21:46
*/
@Service("userDetailsService")
@Slf4j
public class UserDetailService implements UserDetailsService
{
@Autowired
private UsersDao usersDao;
@Override
@Transactional()
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException
{
Users users = usersDao.findByUsername(username);
if(users == null){
log.error("用户名:[{}]不存在",username);
throw new UsernameNotFoundException("用户名不存在");
}
Set<Authorities> authoritiesSet = users.getAuthorities();
ArrayList<GrantedAuthority> list = new ArrayList<>();
for (Authorities authorities : authoritiesSet)
{
list.add(new SimpleGrantedAuthority(authorities.getAuthority()));
}
return new User(users.getUsername(),users.getPassword(),list);
}
}
(9)新建Spring Boot项目的启动类:
package com.shrimpking;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
@SpringBootApplication
@EnableWebSecurity
public class Test17SecurityjpaApplication
{
public static void main(String[] args)
{
SpringApplication.run(Test17SecurityjpaApplication.class, args);
}
}
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>请登录</title>
</head>
<body>
<div>
<form action="/login" th:action="@{/login}" method="post">
<p>
<span>请输入用户名:</span>
<input type="text" id="username" name="username">
</p>
<p>
<span>请输入密码:</span>
<input type="password" id="password" name="password">
</p>
<input type="submit" value="登录">
</form>
</div>
</body>
</html>
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>主页</title>
</head>
<body>
你已经登录成功!
<form action="/loginPage" th:action="@{/logout}" method="post">
<input type="submit" value="退出系统">
</form>
</body>
</html>
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>页面</title>
</head>
<body>
hello页面
</body>
</html>
应在启动项目之前做好数据库的配置工作。本书使用MySQL 5。配置信息的具体位置设置于application.properties文件中。建议读者根据实际需求自行调整相关参数。经确认无误后便可开始项目搭建工作。
访问该页面即可观察到登录界面。通过输入账号admin和密码123456完成登录操作后,则能够查看到相应的权限设置。


全部评论 (0)
还没有任何评论哟~
