Web项目集成SpringBoot+spring security+thymeleaf+hibernate

集成spring security实现登录权限资源控制
项目结构,标准的springboot项目结构
在这里插入图片描述

配置pom文件引入相关jar

<project xmlns="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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.zzp</groupId>
  <artifactId>my-spring-security</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <name>tomato-study</name>
  <description>学习spring security</description>
  
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.3.RELEASE</version>
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</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-data-jpa</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            </dependency>

        <dependency>
            <groupId>org.mariadb.jdbc</groupId>
            <artifactId>mariadb-java-client</artifactId>
            </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                	<source>1.8</source>
                	<target>1.8</target>
                	<encoding>UTF-8</encoding>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

application.yml配置

server:
  port: 8088

spring:
  thymeleaf:
    cache: false
  datasource: 
    url: jdbc:mysql://localhost:3306/spring-security?useUnicode=true&characterEncoding=utf8mb4
    driver-class-name: org.mariadb.jdbc.Driver
    username: root
    password: 123456
  jpa:
    hibernate:
      ddl-auto: update
    database-platform: org.hibernate.dialect.MySQL5Dialect
logging:
  file: spring.log
  level:
    root: INFO

配置security 新建一个类WebSecurityConfig集成 WebSecurityConfigurerAdapter

package com.zzp.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
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.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

import com.zzp.authentication.MyAuthenticationFailHandler;
import com.zzp.authentication.MyAuthenticationSuccessHandler;

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter{

	

    /**
     * 匹配 "/" 路径,不需要权限即可访问
     * 匹配 "/user" 及其以下所有路径,都需要 "USER" 权限
     * 登录地址为 "/login",登录成功默认跳转到页面 "/user"
     * 退出登录的地址为 "/logout",退出成功后跳转到页面 "/login"
     * 默认启用 CSRF这是跨域设置
     */
    /**
     * 注入 自定义的  登录成功处理类
     */
    @Autowired
    private MyAuthenticationSuccessHandler mySuccessHandler;
    @Autowired
    private MyAuthenticationFailHandler myFailHandler;
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                .antMatchers("/").permitAll()
                .antMatchers("/user/**").hasAuthority("USER")
                .antMatchers("/login").permitAll()
                .antMatchers("/logout").permitAll()
                .and()
                .formLogin()
                	.loginPage("/login")
                	.loginProcessingUrl("/loginUser")
                	.successForwardUrl("/user")
                	.successHandler(mySuccessHandler)
                	.failureHandler(myFailHandler)
                .and()
                .logout().logoutUrl("/logout").logoutSuccessUrl("/login");
//                .and()
//                .csrf().disable();
        //这里不用自定义的认证过滤
        //http.addFilterAt(customFromLoginFilter(), UsernamePasswordAuthenticationFilter.class);
                
    }
    
    /**
      * 自定义认证过滤器
     */
    private CustomFromLoginFilter customFromLoginFilter() {
        return new CustomFromLoginFilter("/login");
    }
    


}

新建两个登录成功和失败处理类

package com.zzp.authentication;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
import org.springframework.stereotype.Component;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 自定义登录失败处理器
 * Created by Fant.J.
 */
@Component("myAuthenctiationFailureHandler")
public class MyAuthenticationFailHandler extends SimpleUrlAuthenticationFailureHandler {

    /**
     * 日志
     */
    private Logger logger = LoggerFactory.getLogger(getClass());

     public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) throws IOException, ServletException {

     logger.info("登录失败");
     
     System.out.println("----HHH---->>"+e.getMessage());
     super.setDefaultFailureUrl("/login?error=true");
     super.onAuthenticationFailure(request,response,e);

    }
}

package com.zzp.authentication;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
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;

/**
 * 自定义登录成功处理类
 * Created by Fant.J.
 */
@Component("myAuthenctiationSuccessHandler")
public class MyAuthenticationSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {

    /**
     * 日志
     */
    private Logger logger = LoggerFactory.getLogger(getClass());
    
//    private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();



    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        logger.info("登录成功");
       super.setDefaultTargetUrl("/user");
       super.onAuthenticationSuccess(request, response, authentication);
       //redirectStrategy.sendRedirect(request, response, "/user");

    }
}


新增一个类实现接口UserDetailsService,这个是登录认证处理相关的。

package com.zzp.config;

import com.zzp.entity.UserDO;
import com.zzp.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;

@Service
public class DbUserDetailsService implements UserDetailsService {

    private final UserService userService;
    
    /**
     * 重写PasswordEncoder  接口中的方法,实例化加密策略
     * @return 返回 BCrypt 加密策略
     */
    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }

    @Autowired
    private PasswordEncoder myPasswordEncoder;

    @Autowired
    DbUserDetailsService(UserService userService){
        this.userService = userService;
    }

    
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        UserDO userDO = userService.getByUsername(username);
        if (userDO == null){
        	System.out.println("=========用户不存在!=========>>");
            throw new UsernameNotFoundException("用户不存在!");
        }
        List<SimpleGrantedAuthority> simpleGrantedAuthorities = new ArrayList<SimpleGrantedAuthority>();
        simpleGrantedAuthorities.add(new SimpleGrantedAuthority("USER"));
        String password = userDO.getPassword();
        password = myPasswordEncoder.encode(password);
        System.out.println("=========loadUserByUsername=========>>");
        return new org.springframework.security.core.userdetails.User(userDO.getUsername(), password, simpleGrantedAuthorities);
    }

}

Controller编写
homeController

@Controller
public class HomeController {

    @GetMapping({"/", "/index", "/home"})
    public String root(){
        return "index";
    }

    @GetMapping("/login")
    public String login(){
        return "login";
    }
    

}

UserController

@Controller
public class UserController {

    @RequestMapping("/user")
    public String user(@AuthenticationPrincipal Principal principal, Model model){
    	System.out.println("========user=======>>");
        model.addAttribute("username", principal.getName());
        return "user/user";
    }

}

user实体类

package com.zzp.entity;


import javax.persistence.*;

@Entity
@Table(name = "user")
public class UserDO {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    /**
     * 账号
     */
    private String username;

    /**
     * 密码
     */
    private String password;

    /**
     * 昵称
     */
    private String nickname;

	public Long getId() {
		return id;
	}

	public void setId(Long id) {
		this.id = id;
	}

	public String getUsername() {
		return username;
	}

	public void setUsername(String username) {
		this.username = username;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	public String getNickname() {
		return nickname;
	}

	public void setNickname(String nickname) {
		this.nickname = nickname;
	}
    
    

}

Repository 就是dao 继承CrudRepository

package com.zzp.repository;

import com.zzp.entity.UserDO;
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface UserRepository extends CrudRepository<UserDO, Long> {

    UserDO findByUsername(String username);

}

service

public interface UserService {

    /**
     * 添加新用户
     *
     * username 唯一, 默认 USER 权限
     */
    void insert(UserDO userDO);

    /**
     * 查询用户信息
     * @param username 账号
     * @return UserEntity
     */
    UserDO getByUsername(String username);

}

@Service
@Primary
@Slf4j
public class BaseUserService implements UserService {

    private final UserRepository userRepository;

    public BaseUserService(UserRepository userRepository){
        this.userRepository = userRepository;
    }

    public void insert(UserDO userDO) {
        String username = userDO.getUsername();
        if (exist(username)){
            throw new RuntimeException("用户名已存在!");
        }
       userRepository.save(userDO);
    }

    public UserDO getByUsername(String username) {
        return userRepository.findByUsername(username);
    }

    /**
     * 判断用户是否存在
     */
    private boolean exist(String username){
        UserDO userDO = userRepository.findByUsername(username);
        return (userDO != null);
    }

}

前端页面
user.html

<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>用户中心 | Spring Security Demos</title>
    <link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.css" rel="stylesheet">
</head>

<body style="background-color: #f1f1f1; padding-bottom: 0">

<div th:insert="~{header :: nav}"></div>

<div class="container" style="margin-top: 60px">

    <div style="text-align: center; margin-top: 10%">
        <img src="http://upload.jianshu.io/users/upload_avatars/3424642/fb55f16faaf6.jpg?imageMogr2/auto-orient/strip|imageView2/1/w/240/h/240" alt="avatar" class="img-circle" style="margin: 0 auto">
        <p th:text="${username}" style="margin-top: 25px; font-size: 20; color: crimson">zzp</p>
        <form th:action="@{/logout}" method="post">
            <button class="btn btn-danger" style="margin-top: 20px">退出登录</button>
        </form>
    </div>

</div>

</body>

</html>

head.html

<html xmlns:th="http://www.thymeleaf.org">
<head>
	<meta charset="utf-8">
</head>
<body>
<div th:fragment="nav">
    <nav class="navbar navbar-inverse navbar-fixed-top" style="margin: 0; border: 0">
        <div class="container">
            <div class="navbar-header">
                <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
                    <span class="sr-only">Toggle navigation</span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                    <span class="icon-bar"></span>
                </button>
            </div>
            <div id="navbar" class="navbar-collapse collapse">
                <ul class="nav navbar-nav">
                    <li>
                        <a href="/">首页</a>
                    </li>
                    <li>
                        <a href="https://anoyi.com/" target="_blank">博客</a>
                    </li>
                    <li>
                        <a href="http://www.jianshu.com/u/7b7ec6f2db21" target="_blank">简书</a>
                    </li>
                    <li>
                        <a href="https://github.com/ChinaSilence" target="_blank">Github</a>
                    </li>
                    <li>
                        <a href="http://spring4all.com" target="_blank">源码</a>
                    </li>
                </ul>
                <div class="navbar-form navbar-right">
                    <a class="btn btn-danger" th:href="@{/logout}" th:if="${#httpServletRequest.remoteUser}">退出登录</a>
                    <a class="btn btn-success" th:href="@{/login}" th:unless="${#httpServletRequest.remoteUser}">登录</a>
                </div>
            </div>
        </div>
    </nav>
</div>
</body>
</html>

index.html

<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Spring Security Demos</title>
    <link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.css" rel="stylesheet">
</head>

<body style="padding-bottom: 0">

<div th:insert="~{header :: nav}"></div>

<div class="jumbotron">
    <div class="container" style="padding-top: 30px">
        <h1>Spring Security</h1>
        <p style="margin-top: 20px;">Spring Security 是一个功能强大且可高度自定义的身份验证和访问控制框架,它是保护基于 Spring 的应用的最佳实践,与所有 Spring 项目一样,Spring Security 的真正强大之处在于它可以轻松扩展以满足自定义要求。</p>
        <p style="margin-top: 20px;"><a class="btn btn-primary btn-lg" href="https://spring.io/projects/spring-security" role="button"> 官方文档 &raquo;</a></p>
    </div>
</div>

<div class="container">
    <!-- Example row of columns -->
    <div class="row">
        <div class="col-md-4">
            <h2>Github</h2>
            <p>Spring Security 源码</p>
            <p><a class="btn btn-default" href="https://github.com/spring-projects/spring-security" role="button">查看详情 &raquo;</a></p>
        </div>
        <div class="col-md-4">
            <h2>Stackoverflow</h2>
            <p>Spring Security 相关问题答疑 </p>
            <p><a class="btn btn-default" href="https://stackoverflow.com/search?q=spring-security" role="button">查看详情 &raquo;</a></p>
        </div>
        <div class="col-md-4">
            <h2>Micro</h2>
            <p>Spring Security 在微服务架构下的实践</p>
            <p><a class="btn btn-default" href="https://github.com/ChinaSilence/micro" role="button">查看详情 &raquo;</a></p>
        </div>
    </div>

    <hr>

    <footer>
        <p>&copy; 2019 Power By <a href="https://anoyi.com">Anoyi</a> .</p>
    </footer>
</div> <!-- /container -->

</body>

</html>

login.html

<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>登录 | Spring Security Demos</title>
    <link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.css" rel="stylesheet">
</head>

<body style="background-color: #f1f1f1; padding-bottom: 0">

<div th:insert="~{header :: nav}"></div>

<div class="container" style="margin-top: 60px">

    <div class="row" style="margin-top: 100px">
        <div class="col-md-6 col-md-offset-3">
            <div class="panel panel-primary">
                <div class="panel-heading">
                    <h3 class="panel-title"><span class="glyphicon glyphicon-console"></span>Login</h3>
                </div>
                <div class="panel-body">
                    <form th:action="@{/loginUser}" method="post">
                        <div class="form-group" style="margin-top: 30px">
                            <div class="input-group col-md-6 col-md-offset-3">
                                <div class="input-group-addon"><span class="glyphicon glyphicon-user"></span></div>
                                <input type="text" class="form-control" name="username" id="username" placeholder="账号">
                            </div>
                        </div>
                        <div class="form-group ">
                            <div class="input-group col-md-6 col-md-offset-3">
                                <div class="input-group-addon"><span class="glyphicon glyphicon-lock"></span></div>
                                <input type="password" class="form-control" name="password" id="password"
                                       placeholder="密码">
                            </div>
                        </div>
                        <br>
                        <div th:if="${param.error}">
                            <p style="text-align: center" class="text-danger">登录失败,账号或密码错误!</p>
                        </div>
                        <div th:if="${result}">
                            <p style="text-align: center" class="text-success" th:text="${result}"></p>
                        </div>
                        <div class="form-group">
                            <div class="input-group col-md-6 col-md-offset-3 col-xs-12 ">
                                <button type="submit" class="btn btn-primary btn-block">登录</button>
                            </div>
                        </div>
                        <div class="form-group">
                            <div class="input-group col-md-6 col-md-offset-3" style="text-align: center">
                                <a href="/register">创建账号</a> | 忘记密码?
                            </div>
                        </div>
                    </form>
                </div>
            </div>
        </div>
    </div>

</div>

</body>

</html>

效果图
在这里插入图片描述
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/u013264752/article/details/95319870