本文主要是介绍springboot简单集成Security配置的教程,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
《springboot简单集成Security配置的教程》:本文主要介绍springboot简单集成Security配置的教程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,...
集成Security安全框架
引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.79</version>
</dependency>编写配置类WebSecurityConfig(自定义资源权限规则处理)
package com.lzj.config;
import com.lzj.config.security.MyUserDetailsService;
import com.lzj.handler.auth.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
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.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.cors.CorsUtils;
/**
* <p>
* Security配置类
* </p>
*
* @author:雷子杰
* @date:2022/8/6
*/
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private MyUserDetailsService myUserDetailsService;
@Autowired
private MyAuthenticationFailureHandler myAuthenticationFailureHandler;
@Autowired
private MyAuthenticationSuccessHandler myAuthenticationSuccessHandler;
@Autowired
private LogoutSuccessHandlerImpl logoutSuccessHandler;
@Autowired
private MyAuthenticationEntryPoint myAuthenticationEntryPoint;
@Autowired
private MyAccessDeniedHandler myAccessDeniedHandler;
@Override
public void configure(HttpSecurity http) throws Exception {
// 什么是(cors 预检请求) 就是你要跨域请求得时候 你要预先发一个请求看对面是拦你还是放你
//第1步:解决跨域问题。cors 预检请求放行,让Spring security 放行所有preflight request(cors 预检请求)
http.formLogin().loginProcessingUrl("/login").permitAll();
http.authorizeRequests().requestMatchers(CorsUtils::isPreFlightRequest).permitAll();
//放行掉这个iframe加载
http.headers().frameOptions().disable();
http.logout().logoutUrl("/logout").logoutSuccessHandler(logoutSuccessHandler);
// //禁用csrf
// http.csrf().disable();
// sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
// .and().headers().cacheControl()
//配置权限
http.authorizeRequests()
// 放行注册请求
.antMatchers(HttpMethod.POST,"/user/registerUser").permitAll()
// 放行swagger
.antMatchers("/swagger-ui.html").permitAll()
.anyRequest()
.access(("@dynamicPermission.checkPermisstion(authentication)"))
.and().csrf().disable();
// 自己的过滤器
http.addFilterAt(myUsernamePasswordAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
//第6步:处理异常情况:认证失败和权限不足
http.exceptionHandling().authenticationEntryPoint(myAuthenticationEntryPoint).accessDeniedHandler(myAccessDeniedHandler);
}
//自定义filter交给工厂管理
@Bean
public MyUsernamePasswordAuthenticationFilter myUsernamePasswordAuthenticationFilter() throws Exception {
MyUsernamePasswordAuthenticationFilter filter = new MyUsernamePasswordAuthenticationFilter();
filter.setAuthenticationSuccessHandler(myAuthenticationSuccessHandler);
filter.setAuthenticationFailureHandler(myAuthenticationFailureHandler);
filter.setAuthenticationManager(authenticationManagerBean());
return filter;
}
@Bean
public BCryptPasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
//用户密码加密验证
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService( myUserDetailsService).passwordEncoder(passwordEncoder());
}
}编写配置自定义拦截器(分离式开发)
- MyAuthenticationSuccessHandler(自定义登陆成功处理)
package com.lzj.handler.auth; import com.alibaba.fastjson.JSON; import com.lzj.entity.User; import com.lzj.entity.UserLogin; import com.lzj.enums.ResultInfo; import com.lzj.config.security.UserAuth; import com.lzj.service.UserLoginService; import com.lzj.utils.IpUtil; import com.lzj.utils.Result; import org.apache.dubbo.config.annotation.Reference; import org.springframework.security.core.Authentication; import org.springframework.security.web.authentication.AuthenticationSuccessHandler; 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.Date; /** * <p> * 自定义登陆成功处理 * </p> * * @author:雷子杰 * @date:2022/8/6 */ @Component public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler { @Reference private UserLoginService userLoginService; @Override public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { //验证成功来到这个处理器 //然后获取用户信息 UserAuth user = (UserAuth) authentication.getPrincipal(); response.setContentType("application/json;charset=UTF-8"); response.getWriter().write(JSON.toJSONString(Result.ok() .setCode(ResultInfo.VERIFY_SUCCESS.getCode()) .setMessage(ResultInfo.VERIFY_SUCCESS.getMessage()).data("user", user))); //这下面的是非必须的,这是我数据库中用来存储登陆记录的一个表 //更新用户登录ip地址,最新登录时间 String ipAddress = IpUtil.getIp(request); String ipSource = IpUtil.getIpSource(ipAddress); User loginUser = user.getUser(); UserLogin login = new UserLogin(); login.setAvatar(loginUser.getAvatar()); login.setIpAddress(ipAddress); login.setIpSources(ipSource); login.setNickname(loginUser.getNickname()); login.setLoginTime(new Date()); //查询他上次得登录时间设置为上次登录时间 //这个时间应该设置到Redis中 每次登录就存入redis作为上次登录时间 并且每次更新 //存入表 userLoginService.save(login); } }
- MyAuthenticationFailureHandler(自定义登陆失败处理)
package com.lzj.handler.auth;
import com.alibaba.fastjson.JSON;javascript
import com.lzj.enums.ResultInfo;
import com.lzj.utils.Result;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* <p>
* 自定义登陆失败处理
* </p>
*
* @author:雷子杰
* @date:2022/8/6
*/
@Component
public class MyAuthenticationFailureHandler implements AuthenticationFailureHandler {
@Override
public void onAuthenticationFailure(HttpServletRequest request,
HttpServletResponse response,
AuthenticationException exception) throws IOException, ServletException {
//来到登录失败处理器
//这个返回可以封装一下 就2个不用了
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write(JSON.toJSONString(Result.error()
.setCode(ResultInfo.LOGIN_FAILED.getCode())
.setMessage(ResultInfo.LOGIN_FAILED.getMessage())));
}
}- LogoutSuccessHandlerImpl(自定义认证注销处理)
package com.lzj.handler.auth;
import com.alibaba.fastjson.JSON;
import com.lzj.utils.Result;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* <p>
* 自定义认证注销处理
* </p>
*
* @author:雷子杰
* @date:2022/8/6
*/
@Component
public class LogoutSuccessHandlerImpl implements LogoutSuccessHandler {
@Override
public void onLogoutSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
httpServletResponse.setContentType("application/json;charset=UTF-8");
httpServletResponse.getWriter().write(JSON.toJSONString(Result.ok().setMessage("注销成功")));
}
}- 自定义登录账号密码处理
package com.lzj.handler.auth; import com.fasterXML.jackson.databind.ObjectMapper; import com.lzj.exception.security.MyAuthenticationException; import com.lzj.service.UserService; import org.apache.dubbo.config.annotation.Reference; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.MediaType; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.InputStream; import java.util.Map; /** * <p> * 自定义登录账号密码处理 * </p> * * @author:雷子杰 * @date:2022/8/6 */ public class MyUsernamePasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter { @Reference private UserService userService; @Override public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException { //携带头要是json格式得application/json;charset=UTF-8 //前后端分离 这里是接收的是json串 if (request.getContentType().equals(MediaType.APPLICATION_JSON_UTF8_VALUE) || request.getContentType().equals(MediaType.APPLICATION_JSON_VALUE)){ ObjectMapper objectMapper = new ObjectMapper(); //token对象 UsernamePasswordAuthenticationToken authRequest = null; //取authenticationBean Map<String, String> authenticationBean = null; //用try with resource,方便自动释放资源 //用try with resource,方便自动释放资源 try (InputStream is = request.getInputStream()) { //字符串转集合 authenticationBean = objectMapper.readValue(is, Map.class); } catch (IOException e) { //将异常放到自定义的异常类中 System.out.println(e.getMessage()); throw new MyAuthenticationException(e.getMessage()); } try { if (!authenticationBean.isEmpty()) { //获得账号、密码 String username = authenticationBean.get(SPRING_SECURITY_FORM_USERNAME_KEY); String password = authenticationBean.get(SPRING_SECURITY_FORM_PASSWORD_KEY); //可以验证账号、密码 // System.out.println("username = " + username); // System.out.println("password = " + password); //检测账号、密码是否存在 if (userService.checkLogin(username, password)) { //将账号、密码装入UsernamePasswordAuthenticationToken中 authRequest = new UsernamePasswordAuthenticationToken(username, password); setDetails(request, authRequest); return this.getAuthenticationManager().authenticate(authRequest); } } } catch (AuthenticationException e) { e.printStackTrace(); throw new MyAuthenticationException(e.getMessage()); } throw new MyAuthenticationException("用户或者密码错误"); } return this.attemptAuthentication(request,response); } }
- UserServiceImpl
@Component
@Service
@Slf4j
@Transactional
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
@Autowired
private UserMapper userMapper;
@Autowired
private BCryptPasswordEncoder bCryptPasswordEncoder;
/**
* 检测用户账号密码是否存在
* @param username 用户名
* @param password 密码
* @return 是否存在
*/
@Override
public boolean checkLogin(String username, String password) {
//从用户表里面查出用户 然得到加密的密码
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.select("password").eq("username", username);
User user = userMapper.selectOne(wrapper);
if(user==null){
return false;
}
//这个是加密后的密码 在数据库里面
String userPassword = user.getPassword();
//用加密匹配这个 能匹配返回true 不能返回false
return bCryptPasswordEncoder.matches(password, userPassword);
}
}- 自定义登陆处理
package com.lzj.handler.auth;
import com.alibaba.fastjson.JSON;
import com.lzj.utils.Result;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* <p>
* 自定义登录处理
* </p>
*
* @author:雷子杰
* @date:2022/8/6
*/
@Component
public class MyAuthenticationEntryPoint implements AuthenticationEntryPoint {
/**
*/
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
response.setContentType("application/json;charset=utf-8");
response.getWriter().write(
JSphpON.toJSONString(Result.error().setMessage("请登录")));
}
}- 自定义权限处理
package com.lzj.handler.auth; import com.alibaba.fastjson.JSON; import com.lzj.utils.Result; import org.springframework.security.access.AccessDeniedException; import org.springframework.security.web.access.AccessDeniedHandler; import org.springframework.stereotype.Component; import javax.servlet.ServletException; import javax.servletjavascript.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; /** * <p> * 自定义权限处理 * </p> * * @author:雷子杰 * @date:2022/8/6 */ @Component public class MyAccessDeniedHandler implements AccessDeniedHandler { @Override public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException { response.setContentType("application/json;charset=utf-8"); response.getWriter().write(JSON.toJSONString(Result.error().setMessage("没有权限"))); } }
自定义认证数据源
- 定义Security里的数据源实体类
package com.lzj.config.security;
import com.lzj.entity.User;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import java.util.Collection;
/**
* <p>
* Security里的数据源实体类
* </p>
*
* @author:雷子杰
* @date:2022/8/6
*/
public class UserAuth implements UserDetails {
@Override
public String toString() {
return "UserAuth{" +
"user=" + user +
", authorities=" + authorities +
'}';
}
private User user;
private Collection<? extends GrantedAuthority> authorities;
public UserAuth() {
}
public UserAuth(User user, Collection<? extends GrantedAuthority> authorities) {
this.user = user;
this.authorities = authorities;
}
public User getUser() {
return user;
}
public void setUser(User user) {
this.user = user;
}
public void setAuthorities(Collection<? extends GrantedAuthority> authorities) {
this.authorities = authorities;
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return authorities;
}
@Override
public String getPassword() {
return user.getPassword();
}
@Override
public String getUsername() {
return user.getUsername();
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
}- 自定义数据源配置
package com.lzj.config.security;
import com.lzj.entity.User;
import com.lzj.service.UserService;
import org.apache.dubbo.config.annotation.Reference;
import org.springframework.security.core.GrantedAuthority;
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.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
/**
* <p>
* 自定义数据源配置
* </p>
*
* @author:雷子杰
* @date:2022/8/6
*/
@Service
public class MyUserDetailsService implements UserDetailsService {
@Reference
private UserService userService;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
if(username.isEmpty()){
throw new UsernameNotFoundException("用户名为空");
}
//根据用户名找user
User user = userService.getUserByUsername(username);
if(user!=null){
List<GrantedAuthority> authorities=new ArrayList<>();
//根据用户名查找用户角色列表
List<String> roles = userService.listUserRolesByUsername(username);
//这样写减少对象指针 好一点
SimpleGrantedAuthority simpleGrantedAuthority=null;
for (String role :roles){
simpleGrantedAuthority =new SimpleGrantedAuthority(role);
authorities.add(simpleGrantedAuthority);
}
UserAuth userAuth=new UserAuth();
userAuth.setUser(user);
userAuth.setAuthorities(authorities);
return userAuth;
} else {
throw new UsernameNotFoundException("没有该用户");
}
}
}- UserServiceImpl
@Component
@Service
@Slf4j
@Transactional
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
@Autowired
private UserMapper userMapper;
/**
* 通过用户名获取用户信息
* @param username 用户名
* @return 查询到的信息
*/
@Override
public User getUserByUsername(String username) {
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.eq("username", username);
User user = userMapper.selectOne(wrapper);
return user;
}
/**
* 根据用户名查找权限信息
* @param username 用户名
* @return 权限信息集合
*/
@Override
public List<String> listUserRolesByUsername(String username) {
List<String> roles = userMapper.listUserRolesByUsername(username);
return roles;
}
}- UserMapper.xml
!-- 根据用户名查找该用户的权限-->
<select id="listUserRolesByUsername" resultType="java.lang.String">
select tb_role.role_name
from tb_role, tb_user, tb_role_user
where tb_role.role_id=tb_role_user.role_id
and tb_user.user_id=tb_role_user.user_id
and tb_user.username=#{username}
</select>自定义异常处理
- MyaccessDeniedException
paChina编程ckage com.lzj.exception.security;
import org.springframework.security.access.AccessDeniedException;
/**
* <p>
*
* </p>
*
* @author:雷子杰
* @date:2022/8/6
*/
public class MyaccessDeniedException extends AccessDeniedException {
public MyaccessDeniedException(String msg) {
super(msg);
}
}- MyAuthenticationException
package com.lzj.exception.security;
import org.springframework.security.core.AuthenticationException;
/**
* <p>
*
* </p>
*
* @author:雷子杰
* @date:2022/8/6
*/
public class MyAuthenticationException extends AuthenticationException {
public MyAuthenticationException(String msg) {
super(msg);
}
}权限判断与获取(不一定要添加)
package com.lzj.config.security;
import com.alibaba.fastjson.JSON;
import com.lzj.entity.Api;
import com.lzj.exception.security.MyaccessDeniedException;
import com.lzj.service.UserService;
import com.lzj.utils.RedisUtil;
import org.apache.dubbo.config.annotation.Reference;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.List;
/**
* <p>
* 权限判断与获取
* </p>
*
* @author:雷子杰
* @date:2022/8/6
*/
@Component
public class DynamicPermission {
private final static String PERMISSION_KEY="PERMISSION_KEY";
@Autowired
private RedisUtil redisUtil;
@Autowired
HttpServletRequest request;
@Reference
private UserService userService;
/**
* 判断有访问API的权限
* @param authentication
* @return
* @throws MyaccessDeniedException
*/
public boolean checkPermisstion(Authentication authenticajavascripttion) throws MyaccessDeniedException, IOException {
String name = authentication.getName();
//如果是匿名用户 给匿名用户get权限 查看
if(name.equals("anonymousUser")){
String method = request.getMethod();
if(method.equals("GET")){
return true;
}else{
throw new MyaccessDeniedException("非法操作!");
}
}
//获取当前用户认证信息
Object principal = authentication.getPrincipal();
if(principal instanceof UserDetails) {
UserDetails userDetails = (UserDetails) principal;
//获取到用户名登陆时输入表单的用户名
String username = userDetails.getUsername();
//通过账号获取资源鉴权查看当前用户下的权限信息表
//这个鉴权经查询常要用到 可以加入缓存
//免得
List<Api> apiUrls = getApiUrlByUserName(username);
//AntPathMatcher antPathMatcher = new AntPathMatcher();
//当前访问路径
String requestURI = request.getRequestURI();
//提交类型
String urlMethod = request.getMethod();
//判断当前路径在不在访问资源中
boolean hashAntPath=false;
int hasMethod=-1;
AntPathMatcher pathMatcher = new AntPathMatcher();
//判断当前访问路径在不在权限表里面
for(Api item :apiUrls){
if(pathMatcher.match(item.getUrl(), requestURI)){
hashAntPath=true;
}
hasMethod=item.getMethod().toUpperCase().indexOf(urlMethod.toUpperCase());
if(hashAntPath && hasMethod!=-1){
break;
}
}
boolean res=hashAntPath && hasMethod!=-1;
if(res){
return res;
} else {
throw new MyaccessDeniedException("用户权限不足!");
}
} else{
throw new MyaccessDeniedException("不是UserDetails类型!");
}
}
private List<Api> getApiUrlByUserName(String username) {
List<Api> urlApis=null;
String key= PERMISSION_KEY+"_"+username;
String api = (String) redisUtil.get(key);
//String api = (String) redisTemplate.opsForValue().get(key);
if(api!=null && api!="" ){
//urlApis= JSON.parseobject(api, List.class);
// System.out.println("缓存中"+urlApis);
// return urlApis;
urlApis= JSON.parseArray(api, Api.class);
System.out.println(urlApis);
return urlApis;
}
//获取该用户的api权限
List<Api> apis = userService.getApiUrlByUserName(username);
//加入缓存然后设置过期时间为半个小时
redisUtil.set(key,JSON.toJSONString(apis),1800);
//redisTemplate.opsForValue().set(key, JSON.toJSONString(apis), Duration.ofSeconds(1800L));
return apis;
}
}总结
这篇关于springboot简单集成Security配置的教程的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!