基于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"?>
"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
                        
                    
                
            
        
    

3、设置上下文路径和数据源配置信息
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=true
4、整理脚本样式静态文件 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 List authorityList;
}
MyUser
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 List userList;


}
Authority 6、创建数据访问层接口 创建包:repository,并在该包中创建接口:MyUserRepository,该接口继承了JpaRepository。具体代码如下:
package com.mm.mm7_1.repository;

import com.mm.mm7_1.entity.MyUser;
import org.springframework.data.jpa.repository.JpaRepository;

public interface MyUserRepository extends JpaRepository {
    MyUser findByUsername(String username);
}
MyUserRepository   7、数据库脚本 创建数据库:
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();
        List 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();
    }
}
UserServiceImpl 9、创建控制器类 创建包:controller。在该包中创建控制器类TestSecurityController。具体代码如下。
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("用户名不存在");
        }

        List 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;
    }
}
MyUserSecurityService MyAuthenticationSuccessHandler继承了SimpleUrlAuthenticationSuccessHandler类,并重写了handle(HttpServletRequest request, HttpServletResponse response, Authentication authentication)方法,根据当前认证用户的角色指定对应的URL。 MyAuthenticationSuccessHandler具体代码如下:  
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();
        List 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;
    }
}
MyAuthenticationSuccessHandler MySecurityConfigurerAdapter 类继承了WebSecurityConfigurerAdapter类,并通过重写configure(AuthenticationManagerBuilder auth) 方法实现了用户认证,重写 configure(HttpSecurity http)方法实现用户授权操作。 MySecurityConfigurerAdapter具体代码如下:  
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">
    首页
    "stylesheet" th:href="@{/css/bootstrap.min.css}" />


class="panel panel-primary">
class="panel-heading">

class="panel-title">Spring Security 测试首页

index 注册register.html的代码如下:

"http://www.thymeleaf.org">

    "UTF-8">
    注册页面
    "stylesheet" th:href="@{/css/bootstrap.min.css}" />
    
    


class="container">
class="bg-primary" style="width:100%;height:70px;padding-top: 10px">

"center">用户注册



"@{/register}" name="myForm" method="post" th:object="${userDomain}" class="form-horizontal" role="form">
class="form-group has-success">
class="col-sm-4 col-md-4"> "text" class="form-control" placeholder="请输入你的用户名" th:field="*{username}">
class="form-group has-success">
class="col-sm-4 col-md-4"> "password" class="form-control" placeholder="请输入你的密码" th:field="*{password}">
class="form-group has-success">
class="col-sm-4 col-md-4"> "password" class="form-control" placeholder="请再次输入你的密码" th:field="*{repassword}">

class="form-group">
class="col-sm-offset-2 col-sm-10">
register 登录login.html的代码如下:

"en" xmlns:th="http://www.thymeleaf.org">

    "UTF-8">
    登录页面
    "stylesheet" th:href="@{/css/bootstrap.min.css}" />
    
    


class="container">
class="bg-primary" style="width: 100%;height:70px;padding-top: 10px">

"center">用户登录



"@{/login}" name="myform" method="post" class="form-horizontal" role="form">
if="${param.error!=null}">
class="alert alert-danger">

"red">用户名或者密码错误

if="${param.logout!=null}">
class="alert alert-success">

"red">注销成功

class="form-group has-success">
class="col-sm-4 col-md-4"> "text" class="form-control" placeholder="请输入你的用户名" name="username" id="username">
class="form-group has-success">
class="col-sm-4 col-md-4"> "password" class="form-control" placeholder="请输入你的密码" name="password" id="password">

class="form-group">
class="col-sm-offset-2 col-sm-10">
login 拒绝访问deniedAccess.html的代码如下:

"en"  xmlns:th="http://www.thymeleaf.org">

    "UTF-8">
    首页
    "stylesheet" th:href="@{/css/bootstrap.min.css}" />


class="panel panel-primary">
class="panel-heading">

class="panel-title">拒绝访问页面

class="container">

"${user}">您没有权限访问该页面!您的权限是"${role}">。

"@{/logout}">安全退出
deniedAccess 管理员用户认证成功后访问的页面main.html的代码如下:

"en"  xmlns:th="http://www.thymeleaf.org">

    "UTF-8">
    首页
    "stylesheet" th:href="@{/css/bootstrap.min.css}" />


class="panel panel-primary">
class="panel-heading">

class="panel-title">管理员页面

class="container">

欢迎"${user}">访问该页面!您的权限是"${role}">。



"@{/user/loginSuccess}">去访问用户登录成功界面 "@{/logout}">安全退出
main 普通用户认证成功后访问的页面loginAccess.html的代码如下:

"en"  xmlns:th="http://www.thymeleaf.org">

    "UTF-8">
    首页
    "stylesheet" th:href="@{/css/bootstrap.min.css}" />


class="panel panel-primary">
class="panel-heading">

class="panel-title">登录成功页面

class="container">

欢迎"${user}">登录成功!您的权限是"${role}">。



"@{/admin/main}">去访问管理员功界面 "@{/logout}">安全退出
loginAccess 12、测试应用

 运行Mm71Application的主方法启动项目。

Spring Boot启动后,观察控制台,发现MySecurityConfigurerAdapter的两个config方法都已经被执行,说明自定义的用户认证和用户授权工作已经生效。

在浏览器输入“http://localhost:8080/mm7_1”

 去注册:http://localhost:8080/mm7_1/toRegister

去登录:http://localhost:8080/mm7_1/toLogin

注册用户:mm3

 

 注册成功后跳转到登录界面:

 

 管理员登录成功:

 13、附数据库表