栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

SpringBoot集成Security安全框架

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

SpringBoot集成Security安全框架

项目架构:

SpringBoot 工程
springboot版本 2.1.3
SSM 架构 + Mysql 数据库 + Maven
前端界面 使用的是jsp 并引用了 bootstrap

一:引入Spring Security安全框架

pom.xml 加入引用


		
			org.springframework.boot
			spring-boot-starter-security
		
二:默认拦截

引入Spring Security安全框架 ,
启动项目后,访问项目 会自动拦截到默认的登录界面(自带的)

在控制台和日志中会出现默认密码, 默认帐号user

登录后,跳转到真实访问的界面。
引入spring Security 后,不想开启 拦截校验,可以在项目主方法上加上
exclude= SecurityAutoConfiguration.class

//关闭Security 登录验证功能,Security5.x 后设置
@SpringBootApplication(exclude= SecurityAutoConfiguration.class)
三:自定义各类拦截

实现自定义拦截的关键在于 WebSecurityConfigurerAdapter 类
此类提供各类的配置方法

//WebSecurityConfigurerAdapter 类 部分源码
public abstract class WebSecurityConfigurerAdapter implements
		WebSecurityConfigurer {
		
		//由默认实现{@link #authenticationManager()},可以查看源码的注释,对理解使用有很大帮助 eg:实现权限控制,校验帐号密码
		protected void configure(AuthenticationManagerBuilder auth) throws Exception {
			this.disableLocalConfigureAuthenticationBldr = true;
	    }
		//重写此方法来配置{@link WebSecurity}。eg:忽略某些请求
       public void configure(WebSecurity web) throws Exception {
	   }

	   //覆盖这个方法来配置{@link HttpSecurity},eg:通过重写,配置http来进行拦截的配置
       protected void configure(HttpSecurity http) throws Exception {
		logger.debug("Using default configure(HttpSecurity). If subclassed this will potentially override subclass configure(HttpSecurity).");

		http
			.authorizeRequests()
				.anyRequest().authenticated()
				.and()
			.formLogin().and()
			.httpBasic();
	}

}

首先新建继承 WebSecurityConfigurerAdapter 的配置类

1.基于内存的方式 来拦截
@Configuration
@EnableWebSecurity//开启Spring Security的功能
//prePostEnabled属性决定Spring Security在接口前注解是否可用
//@PreAuthorize,@PostAuthorize等注解,设置为true,
//会拦截加了这些注解的接口
@EnableGlobalMethodSecurity(prePostEnabled=true)
public class WebSecurityConfg extends WebSecurityConfigurerAdapter{
	
	@Override
	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
		//super.configure(auth);
		System.out.println("WebSecurityConfg AuthenticationManagerBuilder");
		// 基于内存的方式,
		//创建两个用户admin/123456 ,user/123456 可以使用这两个用户登录
		auth.inMemoryAuthentication()
		.withUser("admin")//用户名
		.password(passwordEncoder().encode("123456"))//密码
		.roles("ADMIN");//角色
		
		auth.inMemoryAuthentication()
        .withUser("user")//用户名
        .password(passwordEncoder().encode("123456"))//密码
        .roles("USER");//角色
	
	}
 	@Bean
    public PasswordEncoder passwordEncoder(){
        // 使用BCrypt加密密码
        return new BCryptPasswordEncoder();
    }
}


2.查询数据库 用户数据进行 拦截

(1)最关键的是实现 org.springframework.security.core.userdetails.UserDetailsService 接口
加载用户特定数据的核心接口。
它作为一个用户DAO在整个框架中使用,也是DaoAuthenticationProvider使用的策略。
该接口只需要一个只读方法,这简化了对新的数据访问策略的支持。


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

import javax.annotation.Resource;

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.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import com.myjz.clansman.entity.user.UserAndRole;
import com.myjz.clansman.entity.user.UserInfo;
import com.myjz.clansman.service.user.UserService;

@Service
public class CustomUserDetailsService implements UserDetailsService{
	
	@Resource
	private UserService userService;
	@Resource
    private PasswordEncoder passwordEncoder;
	
	@Override
	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

		
		System.out.println("校验 登录------");
		
        //通过userName获取用户信息
		UserInfo userInfo = userService.queryUserByAccount(username);
		if(userInfo == null) {
			 throw new UsernameNotFoundException("not found");
		}
		//查询权限列表
		List roleList = userService.queryUserAndRoleList(userInfo.getUserId());
		//定义权限列表.
        List authorities = new ArrayList<>();
        // 用户可以访问的资源名称(或者说用户所拥有的权限) 注意:必须"ROLE_"开头
        for(UserAndRole useRoleId : roleList) {
        	GrantedAuthority authoritie = new SimpleGrantedAuthority("ROLE_"+useRoleId.getRoleId());
        	authorities.add(authoritie);
        }
       
        User userDetails = new User(userInfo.getAccount(),passwordEncoder.encode(userInfo.getPassword()),authorities);
        
        return userDetails;
		
	}

}

(2)对WebSecurityConfg 进行配置

@Configuration
@EnableWebSecurity//开启Spring Security的功能
//prePostEnabled属性决定Spring Security在接口前注解是否可用
//@PreAuthorize,@PostAuthorize等注解,设置为true,
//会拦截加了这些注解的接口
@EnableGlobalMethodSecurity(prePostEnabled=true)
public class WebSecurityConfg extends WebSecurityConfigurerAdapter{

	@Autowired
	CustomUserDetailsService userDetailsService;
	
	@Override
	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
	 //加载userDetailsService
	 auth.userDetailsService(userDetailsService).passwordEncoder(new BCryptPasswordEncoder());
	
	}

}
3.自定义拦截(自定义登录界面,登录登出拦截器等)-form表单提交

(1).对WebSecurityConfg 进行配置


import org.slf4j.LoggerFactory;
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.method.configuration.EnableGlobalMethodSecurity;
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.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;


@Configuration
@EnableWebSecurity//开启Spring Security的功能
//prePostEnabled属性决定Spring Security在接口前注解是否可用
//@PreAuthorize,@PostAuthorize等注解,设置为true,
//会拦截加了这些注解的接口
@EnableGlobalMethodSecurity(prePostEnabled=true)
public class WebSecurityConfg extends WebSecurityConfigurerAdapter{

	@Autowired
	CustomUserDetailsService userDetailsService;
	
	org.slf4j.Logger log = LoggerFactory.getLogger(WebSecurityConfg.class);
	
	@Override
	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
		//super.configure(auth);
		System.out.println("WebSecurityConfg AuthenticationManagerBuilder");
		auth.userDetailsService(userDetailsService).passwordEncoder(new BCryptPasswordEncoder());
	
	}
	
	
	@Autowired 
	LoginSuccessHandler loginSuccessHandler;
	@Autowired 
	LoginFailureHandler loginFailureHandler;
	
	//http请求拦截配置
	@Override
	protected void configure(HttpSecurity http) throws Exception {
		System.out.println("Using configure(HttpSecurity)");
		
		http//配置权限认证
			.authorizeRequests()
			//配置不拦截路由
			.antMatchers("/css
    @Bean
    public PasswordEncoder passwordEncoder(){
        // 使用BCrypt加密密码
        return new BCryptPasswordEncoder();
    }
	
}

(2).新建 登录成功处理器


import java.io.IOException;
import java.util.Enumeration;

import javax.annotation.Resource;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.http.HttpStatus;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler;
import org.springframework.security.web.savedrequest.RequestCache;
import org.springframework.stereotype.Component;

import com.fasterxml.jackson.databind.ObjectMapper;


@Component("loginSuccessHandler")
public class LoginSuccessHandler extends SavedRequestAwareAuthenticationSuccessHandler {
	
	@Override
	public void onAuthenticationSuccess(HttpServletRequest request,
			HttpServletResponse response, Authentication authentication)
			throws ServletException, IOException {
		 System.out.println("LoginSuccessHandler");
		 //获得前端传到后端的全部参数
		 Enumeration enu = request.getParameterNames();
         while (enu.hasMoreElements()) {
             String paraName = (String) enu.nextElement(); System.out.println("参数- " + paraName + " : " + request.getParameter(paraName));
         }
         logger.info("登录认证成功");
        
        //重定向跳转路径
         this.getRedirectStrategy().sendRedirect(request, response, "/main/main");
	}
	
	
}

(3). 登录失败处理器


import java.io.IOException;

import javax.annotation.Resource;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.security.authentication.AccountExpiredException;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.CredentialsExpiredException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.authentication.LockedException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.web.WebAttributes;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
import org.springframework.stereotype.Component;

import com.fasterxml.jackson.databind.ObjectMapper;



@Component("loginFailureHandler")
public class LoginFailureHandler extends SimpleUrlAuthenticationFailureHandler {

	public void onAuthenticationFailure(HttpServletRequest request,
			HttpServletResponse response, AuthenticationException exception)
			throws IOException, ServletException {
		logger.info("登录失败 处理器");
        //以下写登录失败的逻辑
		String errorInfo = "";
        if (exception instanceof BadCredentialsException ||
                exception instanceof UsernameNotFoundException) {
            errorInfo = "账户名或者密码输入错误!";
        } else if (exception instanceof LockedException) {
            errorInfo = "账户被锁定,请联系管理员!";
        } else if (exception instanceof CredentialsExpiredException) {
            errorInfo = "密码过期,请联系管理员!";
        } else if (exception instanceof AccountExpiredException) {
            errorInfo = "账户过期,请联系管理员!";
        } else if (exception instanceof DisabledException) {
            errorInfo = "账户被禁用,请联系管理员!";
        } else {
            errorInfo = "登录失败!";
        }
        logger.info("登录失败原因:" + errorInfo);
		//System.out.println("错误提示:"+exception.getMessage());
		request.getSession().setAttribute("loginError",errorInfo);
		
		
		//表单提交
        this.saveException(request, exception);
        //重定向跳转路径
        this.getRedirectStrategy().sendRedirect(request, response, "/login/index");
	}
	
	
}

(4).注销登录处理器


import java.io.IOException;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.LoggerFactory;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.web.DefaultRedirectStrategy;
import org.springframework.security.web.RedirectStrategy;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.stereotype.Component;


@Component
public class CustomLogoutSuccessHandler implements LogoutSuccessHandler {

	org.slf4j.Logger log = LoggerFactory.getLogger(CustomLogoutSuccessHandler.class);
	
	private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
	
	@Override
	public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication)
			throws IOException, ServletException {
		System.out.println("注销成功!");
		User user = (User) authentication.getPrincipal();
        String username = user.getUsername();
        log.info("username: {}  is offline now", username);
       
        //重定向跳转路径
        redirectStrategy.sendRedirect(request, response, "/login/index");
		
	}


}

(5).登录控制器Controller

@RestController
@RequestMapping("/login")
public class LoginController {

//跳转登录界面
@RequestMapping(value="/index",method=RequestMethod.GET)
	public ModelAndView index(){
		ModelAndView view = new ModelAndView();
		view.setViewName("/login");
		return view;
	}
}


(6). 新建登录界面 login.jsp 以及 控制跳转

<%@ page language="java" contentType="text/html; charset=UTF-8"
	pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>



登录页





	

(7).配置错误页面 403 404 500 适用于 SpringBoot 2.x
只要实现下面的类即可

@Configuration
public class ErrorPageConfig {
	 @Bean
	    public WebServerFactoryCustomizer webServerFactoryCustomizer() {
	        WebServerFactoryCustomizer webCustomizer = new WebServerFactoryCustomizer() {
	            @Override
	            public void customize(ConfigurableWebServerFactory factory) {
	            	System.out.println("ErrorPageConfig------");
	                ErrorPage[] errorPages = new ErrorPage[] {
	                        new ErrorPage(HttpStatus.FORBIDDEN, "/main/403"),
	                        new ErrorPage(HttpStatus.NOT_FOUND, "/main/404"),
	                        new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR, "/main/500"),
	                };
	                factory.addErrorPages(errorPages);
	            }
	        };
	        return webCustomizer;
	    }
}

很多坑的点和异常 都在代码中有标记 大家要注意 以下特别提醒

  1. http loginPage参数 配置时 一定要加permitAll() 忽略拦截 不然登录界面出不来

  2. http usernameParameter 和passwordParameter 参数 配置时 一定要和界面的name 参数保持一致

  3. http loginProcessingUrl参数 配置 保持表单中action一致,这里还有个点,刚开始我并没有使用jstl 的 标签,直接放了action=“/login/doLogin”,其实url标签默认加了访问路径,如果不用 标签 ,必须 action=“${pageContext.request.contextPath}/login/doLogin” 才可以,注销登录同理

//自定义配置中注销的配置
		http.logout()
			.logoutUrl("/login/doLogout")//配置注销入口 (与登录入口一样,只需表单中action 和此处的一致即可)
			.logoutSuccessHandler(new CustomLogoutSuccessHandler());//注销成功处理器
		     
//注销登录 的界面写法
或者 引入 jstl 标签
  1. 关闭csrf防护 如果不关闭防护的话 登录界面中form 表单必须加上 csrf 的参数

  1. 界面 的样式 都没生效,F12 看浏览器开发工具,发现报错
“because its MIME type ('text/html') is not executable, and strict MIME type checking is enabled”

问题在于我原来配置中没有将 “/js/" ,"/css/” 的路径添加到配置中,导致没有验证的用户没有权限访问

.antMatchers("/css/**", "/js/**","/images/**").permitAll()

权限校验还未添加 未完待续…

引用并参考以下文章
官方中文文档: https://www.springcloud.cc/spring-reference.html Spring
security API 文档:https://docs.spring.io/spring-security/site/docs/5.0.0.M3/api/
官方中文说明文档:https://www.springcloud.cc/spring-security.html
https://www.cnblogs.com/qiantao/p/14605154.html
https://blog.csdn.net/mengmeng_sleep/article/details/105839389
https://blog.csdn.net/qq_44188658/article/details/119782273

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/985293.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号