基于Spring Data JPA的Spring Boot Security的操作实例
目录
1、创建Spring Boot Web应用
2、修改pom.xml文件添加Mysql依赖
3、设置上下文路径和数据源配置信息
4、整理脚本样式静态文件
5、创建用户和权限持久化实体类
6、创建数据访问层接口
7、数据库脚本
8、创建业务层
9、创建控制器类
10、创建应用的安全控制相关实现
11、创建用于测试的视图页面
12、测试应用
13、附数据库表
详细操作
1、创建Spring Boot Web应用
2、修改pom.xml文件添加Mysql依赖
完整POM文件内容:<?xml version="1.0" encoding="UTF-8"?>3、设置上下文路径和数据源配置信息"http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> 4.0.0 org.springframework.boot spring-boot-starter-parent 2.6.4 com.mm mm7_1 0.0.1-SNAPSHOT mm7_1 Demo project for Spring Boot 1.8 org.springframework.boot spring-boot-starter-data-jpa org.springframework.boot spring-boot-starter-security org.springframework.boot spring-boot-starter-thymeleaf org.springframework.boot spring-boot-starter-web org.thymeleaf.extras thymeleaf-extras-springsecurity5 mysql mysql-connector-java 8.0.11 org.projectlombok lombok true org.springframework.boot spring-boot-starter-test test org.springframework.security spring-security-test test org.springframework.boot spring-boot-maven-plugin org.projectlombok lombok
server.servlet.context-path=/mm7_1 spring.datasource.url=jdbc:mysql://localhost:3306/springtest?serverTimezone=UTC&characterEncoding=utf8&useUnicode=true&useSSL=false spring.datasource.username=root spring.datasource.password=test.1234 spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver logging.level.com.mm.mm7_1.repository=debug spring.jpa.database=mysql spring.jpa.show-sql=true spring.jpa.hibernate.ddl-auto=update spring.jackson.serialization.indent-output=true spring.thymeleaf.cache=false logging.level.org.springframework.security=trace spring.main.allow-circular-references=true4、整理脚本样式静态文件 5、创建用户和权限持久化实体类
创建包:entity,并在该包中创建持久化实体类MyUser和Authrity。
MyUser:保存用户数据,用户名唯一。
Authrity:保存权限信息。
用户和权限是多对多关系。
package com.mm.mm7_1.entity; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import lombok.Data; import javax.persistence.*; import java.io.Serializable; import java.util.List; @Data @Entity @Table(name = "user") @JsonIgnoreProperties(value = {"hibernateLazyInitializer"}) public class MyUser implements Serializable { private static final long serialVersionUID=1L; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private int id; private String username; private String password; @Transient private String repassword; @ManyToMany(cascade = {CascadeType.PERSIST},fetch = FetchType.EAGER) @JoinTable(name = "user_authority",joinColumns = @JoinColumn(name = "user_id"), inverseJoinColumns = @JoinColumn(name = "authority_id")) private ListMyUserauthorityList; }
package com.mm.mm7_1.entity; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import lombok.Data; import javax.persistence.*; import java.io.Serializable; import java.util.List; @Data @Entity @Table(name = "authority") @JsonIgnoreProperties(value = {"hibernateLazyInitializer"}) public class Authority implements Serializable { private static final long serialVersionUID=1L; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private int id; @Column(nullable = false) private String name; @ManyToMany(mappedBy = "authorityList") @JsonIgnore private ListAuthority 6、创建数据访问层接口 创建包:repository,并在该包中创建接口:MyUserRepository,该接口继承了JpaRepository。具体代码如下:userList; }
package com.mm.mm7_1.repository; import com.mm.mm7_1.entity.MyUser; import org.springframework.data.jpa.repository.JpaRepository; public interface MyUserRepository extends JpaRepositoryMyUserRepository 7、数据库脚本 创建数据库:{ MyUser findByUsername(String username); }
CREATE DATABASE springtest CHARACTER SET utf8 COLLATE utf8_general_ci;8、创建业务层
创建包:service。在该包中创建UserService接口和UserServiceImpl实现类。
UserService代码如下:
package com.mm.mm7_1.service; import com.mm.mm7_1.entity.MyUser; import org.springframework.ui.Model; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public interface UserService { public String register(MyUser userDomain); public String loginSuccess(Model model); public String main(Model model); public String deniedAccess(Model model); public String logout(HttpServletRequest request, HttpServletResponse response); }UserService
UserServiceImpl代码如下:
package com.mm.mm7_1.service; import com.mm.mm7_1.entity.Authority; import com.mm.mm7_1.entity.MyUser; import com.mm.mm7_1.repository.MyUserRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler; import org.springframework.stereotype.Service; import org.springframework.ui.Model; import sun.plugin.liveconnect.SecurityContextHelper; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.ArrayList; import java.util.List; @Service public class UserServiceImpl implements UserService { @Autowired private MyUserRepository myUserRepository; @Override public String register(MyUser userDomain) { String username=userDomain.getUsername(); ListUserServiceImpl 9、创建控制器类 创建包:controller。在该包中创建控制器类TestSecurityController。具体代码如下。authorityList=new ArrayList<>(); if("admin".equals(username)){ Authority authority1=new Authority(); Authority authority2=new Authority(); //authority1.setId(1); authority1.setName("ROLE_ADMIN"); authority2.setName("ROLE_DBA"); authorityList.add(authority1); authorityList.add(authority2); }else{ Authority authority2=new Authority(); //authority1.setId(1); authority2.setName("ROLE_USER"); authorityList.add(authority2); } userDomain.setAuthorityList(authorityList); String secret=new BCryptPasswordEncoder().encode(userDomain.getPassword()); userDomain.setPassword(secret); MyUser mu=myUserRepository.save(userDomain); if(mu!=null){ return "/login"; }else { return "/register"; } } /** * 用户登录成功 * @param model * @return */ @Override public String loginSuccess(Model model) { model.addAttribute("user",getUname()); model.addAttribute("role",getAuthorities()); return "/user/loginSuccess"; } /** * 管理员登录成功 * @param model * @return */ @Override public String main(Model model) { model.addAttribute("user",getUname()); model.addAttribute("role",getAuthorities()); return "/admin/main"; } /** * 没有权限访问 * @param model * @return */ @Override public String deniedAccess(Model model) { model.addAttribute("user",getUname()); model.addAttribute("role",getAuthorities()); return "deniedAccess"; } /** * 注销用户 * @param request * @param response * @return */ @Override public String logout(HttpServletRequest request, HttpServletResponse response) { Authentication authentication=SecurityContextHolder.getContext().getAuthentication(); if(authentication!=null){ new SecurityContextLogoutHandler().logout(request,response,authentication); } return "redirect:/login?logout"; } private String getUname(){ return SecurityContextHolder.getContext().getAuthentication().getName(); } private String getAuthorities(){ Authentication authentication=SecurityContextHolder.getContext().getAuthentication(); List roles=new ArrayList<>(); for(GrantedAuthority ga :authentication.getAuthorities()){ roles.add(ga.getAuthority()); } return roles.toString(); } }
package com.mm.mm7_1.controller; import com.mm.mm7_1.entity.MyUser; import com.mm.mm7_1.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.RequestMapping; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @Controller public class TestSecurityController { @Autowired private UserService userService; @RequestMapping("/") public String index(){ return "/index"; } @RequestMapping("/toLogin") public String toLogin(){ return "/login"; } @RequestMapping("/toRegister") public String toRegister(@ModelAttribute("userDomain") MyUser userDomain){ return "/register"; } @RequestMapping("/register") public String register(@ModelAttribute("userDomain") MyUser userDomain){ return userService.register(userDomain); } @RequestMapping("/login") public String login(){ return "/login"; } @RequestMapping("/user/loginSuccess") public String loginSuccess(Model model){ return userService.loginSuccess(model); } @RequestMapping("/admin/main") public String main(Model model){ return userService.main(model); } @RequestMapping("/logout") public String logout(HttpServletRequest request, HttpServletResponse response){ return userService.logout(request, response); } @RequestMapping("/deniedAccess") public String deniedAccess(Model model){ return userService.deniedAccess(model); } }TestSecurityController 10、创建应用的安全控制相关实现
创建包:security。在该包中创建MyUserSecurityService、MyAuthenticationSuccessHandler、MySecurityConfigurerAdapter类。
MyUserSecurityService实现了UserDetailsService接口,并通过重写loadUserByUsername(String username)方法查询对应的用户,并将用户名、密码、权限等认证相关的信息封装在UserDetails对象中。
MyUserSecurityService具体代码如下:
package com.mm.mm7_1.security; import com.mm.mm7_1.entity.Authority; import com.mm.mm7_1.entity.MyUser; import com.mm.mm7_1.repository.MyUserRepository; 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 java.util.ArrayList; import java.util.List; @Service public class MyUserSecurityService implements UserDetailsService { @Autowired private MyUserRepository myUserRepository; @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { MyUser myUser=myUserRepository.findByUsername(username); if(myUser==null){ throw new UsernameNotFoundException("用户名不存在"); } ListMyUserSecurityService MyAuthenticationSuccessHandler继承了SimpleUrlAuthenticationSuccessHandler类,并重写了handle(HttpServletRequest request, HttpServletResponse response, Authentication authentication)方法,根据当前认证用户的角色指定对应的URL。 MyAuthenticationSuccessHandler具体代码如下:authorities=new ArrayList<>(); List rules=myUser.getAuthorityList(); for(Authority authority :rules){ GrantedAuthority sg=new SimpleGrantedAuthority(authority.getName()); authorities.add(sg); } User su=new User(myUser.getUsername(),myUser.getPassword(),authorities); return su; } }
package com.mm.mm7_1.security; import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; import org.springframework.security.web.DefaultRedirectStrategy; import org.springframework.security.web.RedirectStrategy; import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler; import org.springframework.stereotype.Component; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.List; @Component public class MyAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler { private RedirectStrategy redirectStrategy=new DefaultRedirectStrategy(); @Override protected void handle(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { String tagerUrl=getTargetUrl(authentication); redirectStrategy.sendRedirect(request,response,tagerUrl); //super.handle(request, response, authentication); } protected String getTargetUrl(Authentication authentication){ String url=""; Collection<? extends GrantedAuthority> authorities=authentication.getAuthorities(); ListMyAuthenticationSuccessHandler MySecurityConfigurerAdapter 类继承了WebSecurityConfigurerAdapter类,并通过重写configure(AuthenticationManagerBuilder auth) 方法实现了用户认证,重写 configure(HttpSecurity http)方法实现用户授权操作。 MySecurityConfigurerAdapter具体代码如下:roles=new ArrayList<>(); for(GrantedAuthority au : authorities){ roles.add(au.getAuthority()); } if(roles.contains("ROLE_USER")){ url="/user/loginSuccess"; } else if(roles.contains("ROLE_ADMIN")){ url="/admin/main"; }else{ url="/deniedAccess"; } return url; } }
package com.mm.mm7_1.security; 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.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; @Configuration public class MySecurityConfigurerAdapter extends WebSecurityConfigurerAdapter { @Autowired private MyUserSecurityService myUserSecurityService; @Autowired private PasswordEncoder passwordEncoder; @Autowired private AuthenticationProvider authenticationProvider; @Autowired private MyAuthenticationSuccessHandler myAuthenticationSuccessHandler; @Bean public PasswordEncoder passwordEncoder(){ return new BCryptPasswordEncoder(); } @Bean public AuthenticationProvider authenticationProvider(){ DaoAuthenticationProvider provider=new DaoAuthenticationProvider(); provider.setHideUserNotFoundExceptions(false); provider.setUserDetailsService(myUserSecurityService); provider.setPasswordEncoder(passwordEncoder); return provider; } @Override protected void configure(AuthenticationManagerBuilder auth) throws Exception { //super.configure(auth); System.out.println("configure(AuthenticationManagerBuilder auth)"); auth.authenticationProvider(authenticationProvider); } @Override protected void configure(HttpSecurity http) throws Exception { //super.configure(http); System.out.println(" configure(HttpSecurity http)"); http.authorizeRequests() .antMatchers("/toLogin","/toRegister","/","/login","/register","/css/**","/fonts/**","/js/**").permitAll() .antMatchers("/user/**").hasRole("USER") .antMatchers("/admin/**").hasAnyRole("ADMIN","DBA") .anyRequest().authenticated() .and() .formLogin() .loginPage("/login").successHandler(myAuthenticationSuccessHandler) .usernameParameter("username").passwordParameter("password") .failureForwardUrl("/login?error") .and() .logout().permitAll() .and() .exceptionHandling().accessDeniedPage("/deniedAccess"); } }MySecurityConfigurerAdapter 11、创建用于测试的视图页面 在src/main/resources/templates目录下创建应用的首页、注册、登录以及拒绝访问页面; 在src/main/resources/templates/admin目录下创建应用的管理员用户认证成功后访问的页面; 在src/main/resources/templates/user目录下创建应用的普通用户认证成功后访问的页面。
首页index.html的代码如下:
"en" xmlns:th="http://www.thymeleaf.org"> "UTF-8">index 注册register.html的代码如下:首页 "stylesheet" th:href="@{/css/bootstrap.min.css}" />class="panel panel-primary">class="panel-heading">class="panel-title">Spring Security 测试首页
"http://www.thymeleaf.org"> "UTF-8">register 登录login.html的代码如下:注册页面 "stylesheet" th:href="@{/css/bootstrap.min.css}" />class="container">class="bg-primary" style="width:100%;height:70px;padding-top: 10px">"center">用户注册
"en" xmlns:th="http://www.thymeleaf.org"> "UTF-8">login 拒绝访问deniedAccess.html的代码如下:登录页面 "stylesheet" th:href="@{/css/bootstrap.min.css}" />class="container">class="bg-primary" style="width: 100%;height:70px;padding-top: 10px">"center">用户登录
"en" xmlns:th="http://www.thymeleaf.org"> "UTF-8">deniedAccess 管理员用户认证成功后访问的页面main.html的代码如下:首页 "stylesheet" th:href="@{/css/bootstrap.min.css}" />class="panel panel-primary">class="panel-heading">class="panel-title">拒绝访问页面
"en" xmlns:th="http://www.thymeleaf.org"> "UTF-8">main 普通用户认证成功后访问的页面loginAccess.html的代码如下:首页 "stylesheet" th:href="@{/css/bootstrap.min.css}" />class="panel panel-primary">class="panel-heading">class="panel-title">管理员页面
class="container">
"en" xmlns:th="http://www.thymeleaf.org"> "UTF-8">loginAccess 12、测试应用首页 "stylesheet" th:href="@{/css/bootstrap.min.css}" />class="panel panel-primary">class="panel-heading">class="panel-title">登录成功页面
运行Mm71Application的主方法启动项目。
Spring Boot启动后,观察控制台,发现MySecurityConfigurerAdapter的两个config方法都已经被执行,说明自定义的用户认证和用户授权工作已经生效。在浏览器输入“http://localhost:8080/mm7_1”
去注册:http://localhost:8080/mm7_1/toRegister
去登录:http://localhost:8080/mm7_1/toLogin
注册用户:mm3
注册成功后跳转到登录界面:
管理员登录成功:
13、附数据库表