spring boot 整合 spring security

项目结构

添加依赖

pom 复制代码
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.9.RELEASE</version>
        <relativePath/>
    </parent>

     <dependency>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-freemarker</artifactId>
         <version>2.4.2</version>
     </dependency>

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

修改配置文件

yml 复制代码
spring:
  freemarker:
    template-loader-path: classpath:/templates/
    charset: UTF-8
    cache: false
    suffix: .ftl

创建登录页

login.ftl

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登录页</title>
</head>
<body>
<form id="formLogin" method="post">
    用户名<input type="text" name="username" value="admin"/>
    密码<input type="text" name="password" value="123456"/>
    <input type="checkbox" name="remember-me" value="true"/>记住我
    <input type="button" onclick="login()" value="login"/>
</form>
</body>
<script type="text/javascript" src="js/jquery.min.js"></script>
<script>
    function login() {
        $.ajax({
            type: "POST",//方法类型
            dataType: "json",//服务器预期返回类型
            url: "/login",   // 登录url
            data: $("#formLogin").serialize(),
            success: function (data) {
                console.log(data)
                if (data.code == 200) {
                    window.location.href = "/";
                } else {
                    alert(data.message);
                }
            }
        });
    }
</script>
</html>

创建首页

index.ftl

html 复制代码
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>首页</title>
</head>
<body>
我是首页
</body>
</html>

创建SecurityConfiguration类

java 复制代码
package com.lgg.config;

import com.lgg.service.MyAuthenticationService;
import com.lgg.service.MyUserDetailsService;
import org.springframework.beans.factory.annotation.Autowired;
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.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;

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

/**
 * Security配置类
 */
@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Autowired
    private MyUserDetailsService myUserDetailsService;
    @Autowired
    private MyAuthenticationService myAuthenticationService;

    /**
     * http请求处理方法
     *
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests().antMatchers("/toLoginPage", "/code/**").permitAll()//放行登录页面
                .anyRequest().authenticated()//所有请求都需要登录认证才能访问;
                .and().formLogin()//开启表单认证
                .loginPage("/toLoginPage")//自定义登录页面
                .loginProcessingUrl("/login")// 登录处理Url
                .usernameParameter("username").passwordParameter("password") //修改自定义表单name值.
                .successHandler(myAuthenticationService)//自定义登录成功处理
                .failureHandler(myAuthenticationService)//自定义登录失败处理
//                .defaultSuccessUrl("/")
//                .successForwardUrl("/")
                .and().logout().logoutUrl("/logout")//设置退出url
                .logoutSuccessHandler(myAuthenticationService)//自定义退出处理
                .and().rememberMe().rememberMeParameter("remember-me")// 自定义表单name值
                .tokenValiditySeconds(2 * 7 * 24 * 60 * 60)// 两周
                .tokenRepository(getPersistentTokenRepository())// 设置tokenRepository
                .and().csrf().disable(); // 关闭csrf防护

        // 允许iframe加载页面
        http.headers().frameOptions().sameOrigin();
    }

    @Autowired
    DataSource dataSource;

    /**
     * 持久化token,负责token与数据库之间的相关操作
     *
     * @return
     */
    @Bean
    public PersistentTokenRepository getPersistentTokenRepository() {
        JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();
        tokenRepository.setDataSource(dataSource);//设置数据源
        // 启动时创建一张表, 第一次启动的时候创建, 第二次启动的时候需要注释掉, 否则会报错
//        tokenRepository.setCreateTableOnStartup(true);
        return tokenRepository;
    }


    /**
     * WebSecurity
     *
     * @param web
     * @throws Exception
     */
    @Override
    public void configure(WebSecurity web) throws Exception {
        //解决静态资源被拦截的问题
        web.ignoring().antMatchers("/css/**", "/images/**", "/js/**", "/favicon.ico");
    }

    /**
     * 身份验证管理器
     *
     * @param auth
     * @throws Exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(myUserDetailsService);// 使用自定义用户认证
    }


}

创建MyAuthenticationService类

java 复制代码
package com.lgg.service;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.DefaultRedirectStrategy;
import org.springframework.security.web.RedirectStrategy;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

/**
 * 自定义登录成功或失败处理类
 */
@Service
public class MyAuthenticationService implements AuthenticationSuccessHandler, AuthenticationFailureHandler, LogoutSuccessHandler {

    @Autowired
    ObjectMapper objectMapper;
    private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        System.out.println("登录成功后续处理....");
//        redirectStrategy.sendRedirect(request, response, "/");
//
        Map result = new HashMap();
        result.put("code", HttpStatus.OK.value());// 设置响应码
        result.put("message", "登录成功");// 设置响应信息
        response.setContentType("application/json;charset=UTF-8");
        response.getWriter().write(objectMapper.writeValueAsString(result));
    }

    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
        System.out.println("登录失败后续处理....");
        //redirectStrategy.sendRedirect(request, response, "/toLoginPage");
//        Map result = new HashMap();
//        result.put("code", HttpStatus.UNAUTHORIZED.value());// 设置响应码
//        result.put("message", exception.getMessage());// 设置错误信息
//        response.setContentType("application/json;charset=UTF-8");
//        response.getWriter().write(objectMapper.writeValueAsString(result));
    }

    @Override
    public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        System.out.println("退出成功后续处理....");
        redirectStrategy.sendRedirect(request, response, "/toLoginPage");
    }

}

创建MyUserDetailsService类

java 复制代码
package com.lgg.service;


import com.lgg.entity.User;
import com.lgg.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
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.Collection;

/**
 * 基于数据库中完成认证
 */
@Service
public class MyUserDetailsService implements UserDetailsService {

    @Autowired
    UserService userService;

    /**
     * 根据username查询用户实体
     *
     * @param username
     * @return
     * @throws UsernameNotFoundException
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        User user = userService.findByUsername(username);
        if (user == null) {
            throw new UsernameNotFoundException(username);// 用户名没有找到
        }
        // 先声明一个权限集合, 因为构造方法里面不能传入null
        Collection<? extends GrantedAuthority> authorities = new ArrayList<>();
        // 需要返回一个SpringSecurity的UserDetails对象
        UserDetails userDetails =
                new org.springframework.security.core.userdetails.User(user.getUsername(),
//                        "{noop}" + user.getPassword(),// {noop}表示不加密认证。
                        "{bcrypt}" + user.getPassword(),
                        true, // 用户是否启用 true 代表启用
                        true,// 用户是否过期 true 代表未过期
                        true,// 用户凭据是否过期 true 代表未过期
                        true,// 用户是否锁定 true 代表未锁定
                        authorities);
        return userDetails;
    }
}

创建HelloSecurityController类

java 复制代码
package com.lgg.controller;


import com.lgg.entity.User;
import com.lgg.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.RememberMeAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationException;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

/**
 * security入门案例
 */
@Controller
public class HelloSecurityController {


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

    /**
     * 获取当前登录用户
     *
     * @return
     */
    @RequestMapping("/abc")
//    @ResponseBody
    public String getCurrentUser() {
        UserDetails userDetails = (UserDetails)
                SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        System.out.println("abc");
        return "abc";
    }

    /**
     * 获取当前登录用户
     *
     * @return
     */
    @RequestMapping("/loginUser1")
    @ResponseBody
    public UserDetails getCurrentUser1() {
        UserDetails userDetails = (UserDetails)
                SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        return userDetails;
    }

    /**
     * 获取当前登录用户
     *
     * @return
     */
    @RequestMapping("/loginUser2")
    @ResponseBody
    public UserDetails getCurrentUser(Authentication authentication) {
        UserDetails userDetails = (UserDetails) authentication.getPrincipal();
        return userDetails;
    }

    /**
     * 获取当前登录用户
     *
     * @return
     */
    @RequestMapping("/loginUser3")
    @ResponseBody
    public UserDetails getCurrentUser(@AuthenticationPrincipal UserDetails userDetails) {
        return userDetails;
    }

    @Autowired
    private UserService userService;

    /**
     * 根据用户ID查询用户
     *
     * @return
     */
    @GetMapping("/{id}")
    @ResponseBody
    public User getById(@PathVariable Integer id) {
        //获取认证信息
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        // 判断认证信息是否来源于RememberMe
        if (RememberMeAuthenticationToken.class.isAssignableFrom(authentication.getClass())) {
            throw new RememberMeAuthenticationException("认证信息来源于RememberMe,请重新登录");
        }
        User user = userService.getById(id);
        return user;
    }


}

启动项目验证

访问任意接口,都会被路由到登录页面login.ftl,只有登录成功后,才能正常访问其他接口。

相关推荐
JH30734 小时前
SpringBoot 优雅处理金额格式化:拦截器+自定义注解方案
java·spring boot·spring
qq_12498707537 小时前
基于SSM的动物保护系统的设计与实现(源码+论文+部署+安装)
java·数据库·spring boot·毕业设计·ssm·计算机毕业设计
Coder_Boy_7 小时前
基于SpringAI的在线考试系统-考试系统开发流程案例
java·数据库·人工智能·spring boot·后端
2301_818732067 小时前
前端调用控制层接口,进不去,报错415,类型不匹配
java·spring boot·spring·tomcat·intellij-idea
汤姆yu10 小时前
基于springboot的尿毒症健康管理系统
java·spring boot·后端
暮色妖娆丶11 小时前
Spring 源码分析 单例 Bean 的创建过程
spring boot·后端·spring
biyezuopinvip12 小时前
基于Spring Boot的企业网盘的设计与实现(任务书)
java·spring boot·后端·vue·ssm·任务书·企业网盘的设计与实现
JavaGuide12 小时前
一款悄然崛起的国产规则引擎,让业务编排效率提升 10 倍!
java·spring boot
figo10tf13 小时前
Spring Boot项目集成Redisson 原始依赖与 Spring Boot Starter 的流程
java·spring boot·后端
zhangyi_viva13 小时前
Spring Boot(七):Swagger 接口文档
java·spring boot·后端