赞
踩
Spring Security是基于Spring生态圈的,用于提供安全访问控制解决方案的框架。Spring Security的安 全管理有两个重要概念,分别是Authentication(认证)和Authorization(授权)。 为了方便Spring Boot项目的安全管理,Spring Boot对Spring Security安全框架进行了整合支持,并提 供了通用的自动化配置,从而实现了Spring Security安全框架中包含的多数安全管理功能。
Spring Security登录认证主要涉及两个重要的接口 UserDetailService和UserDetails接口。
UserDetailService接口主要定义了一个方法 loadUserByUsername(String username)用于完成用户信息的查 询,其中username就是登录时的登录名称,登录认证时,需要自定义一个实现类实现UserDetailService接 口,完成数据库查询,该接口返回UserDetail。
UserDetail主要用于封装认证成功时的用户信息,即UserDetailService返回的用户信息,可以用Spring
自己的User对象,但是最好是实现UserDetail接口,自定义用户对象。
1. 自定UserDetails类:当实体对象字段不满足时需要自定义UserDetails,一般都要自定义
UserDetails。
2. 自定义UserDetailsService类,主要用于从数据库查询用户信息。
3. 创建登录认证成功处理器,认证成功后需要返回JSON数据,菜单权限等。
4. 创建登录认证失败处理器,认证失败需要返回JSON数据,给前端判断。
5. 创建匿名用户访问无权限资源时处理器,匿名用户访问时,需要提示JSON。
6. 创建认证过的用户访问无权限资源时的处理器,无权限访问时,需要提示JSON。
7. 配置Spring Security配置类,把上面自定义的处理器交给Spring Security。
在pom.xml文件中添加Spring Security核心依赖,代码如下所
- <dependency>
-
- <groupId>org.springframework.boot</groupId>
-
- <artifactId>spring-boot-starter-security</artifactId>
- </dependency>
当实体对象字段不满足时Spring Security认证时,需要自定义UserDetails。
1. 将User类实现UserDetails接口
2. 将原有的isAccountNonExpired、isAccountNonLocked、isCredentialsNonExpired和isEnabled属性修 改成boolean类型,同时添加authorities属性。
- @Data
- @TableName("sys_user")
-
- public class User implements Serializable, UserDetails {
-
- //省略原有的属性......
-
-
- /**
- * 帐户是否过期(1 未过期,0已过期)
- */
-
- private boolean isAccountNonExpired = true;
- /**
- * 帐户是否被锁定(1 未过期,0已过期)
- */
-
- private boolean isAccountNonLocked = true;
- /**
- * 密码是否过期(1 未过期,0已过期)
- */
-
- private boolean isCredentialsNonExpired = true;
- /**
- * 帐户是否可用(1 可用,0 删除用户)
- */
-
- private boolean isEnabled = true;
- /**
- * 权限列表
- */
-
- @TableField(exist = false)
- Collection<? extends GrantedAuthority> authorities;
- public interface UserService extends IService<User> {
- /**
- * 根据用户名查询用户信息
- * @param userName
- * @return
- */
-
- User findUserByUserName(String userName);
- }
- package com.manong.service.impl;
-
- import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
- import com.manong.entity.User;
- import com.manong.dao.UserMapper;
- import com.manong.service.UserService;
- import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
- import org.springframework.stereotype.Service;
- import org.springframework.transaction.annotation.Transactional;
-
- /**
- * <p>
- * 服务实现类
- * </p>
- *
- * @author lemon
- * @since 2022-12-06
- */
- @Service
- @Transactional
- public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
-
- @Override
- public User findUserByUserName(String username) {
- //创建条件构造器对象
- QueryWrapper queryWrapper=new QueryWrapper();
- queryWrapper.eq("username",username);
- //执行查询
- return baseMapper.selectOne(queryWrapper);
- }
- }
- package com.manong.config.security.service;
-
- import com.manong.entity.Permission;
- import com.manong.entity.User;
- import com.manong.service.PermissionService;
- import com.manong.service.UserService;
- import org.springframework.security.core.GrantedAuthority;
- import org.springframework.security.core.authority.AuthorityUtils;
- 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.Component;
-
- import javax.annotation.Resource;
- import java.util.List;
- import java.util.Objects;
- import java.util.stream.Collectors;
-
- /*
- * 用户认证处理器类
- * */
- @Component
- public class CustomerUserDetailService implements UserDetailsService {
-
- @Resource
- private UserService userService;
-
- @Resource
- private PermissionService permissionService;
-
-
- public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException{
- //根据对象查找用户信息
- User user = userService.findUserByUserName(username);
- //判断对象是否为空
- if(user==null){
- throw new UsernameNotFoundException("用户的账号密码错误");
- }
- //查询当前登录用户拥有权限列表
- List<Permission> permissionList = permissionService.findPermissionListByUserId(user.getId());
- //获取对应的权限编码
- List<String> codeList = permissionList.stream()
- .filter(Objects::nonNull)
- .map(item -> item.getCode())
- .filter(Objects::nonNull)
- .collect(Collectors.toList());
- //将权限编码转换成数据
- String [] strings=codeList.toArray(new String[codeList.size()]);
- //设置权限列表
- List<GrantedAuthority> authorityList = AuthorityUtils.createAuthorityList(strings);
- //将权限列表设置给User
- user.setAuthorities(authorityList);
- //设置该用户拥有的菜单
- user.setPermissionList(permissionList);
- //查询成功
- return user;
- }
- }
4.1.成功
- package com.manong.config.security.handler;
-
-
- import com.alibaba.fastjson.serializer.SerializerFeature;
- import com.manong.entity.User;
- import com.manong.utils.JwtUtils;
- import com.manong.utils.LoginResult;
- import com.manong.utils.ResultCode;
- import io.jsonwebtoken.Jwts;
- import org.springframework.security.core.Authentication;
- import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
- import org.springframework.stereotype.Component;
- import sun.net.www.protocol.http.AuthenticationHeader;
-
- import javax.annotation.Resource;
- import javax.servlet.ServletException;
- import javax.servlet.ServletOutputStream;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import java.io.IOException;
- import java.nio.charset.StandardCharsets;
-
- import com.alibaba.fastjson.JSON;
-
- /*
- * 登录认证成功处理器类
- * */
- @Component
- public class LoginSuccessHandler implements AuthenticationSuccessHandler {
- @Resource
- private JwtUtils jwtUtils;
- @Override
- public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
- //设置相应编码格式
- response.setContentType("application/json;charset-utf-8");
- //获取当前登录用户的信息
- User user = (User) authentication.getPrincipal();
- //创建token对象
- String token = jwtUtils.generateToken(user);
- //设置token的秘钥和过期时间
- long expireTime = Jwts.parser()
- .setSigningKey(jwtUtils.getSecret())
- .parseClaimsJws(token.replace("jwt_", ""))
- .getBody().getExpiration().getTime();//设置过期时间
- //创建LOgin登录对象
- LoginResult loginResult=new LoginResult(user.getId(), ResultCode.SUCCESS,token,expireTime);
- //将对象转换成json格式
- //消除循环引用
- String result = JSON.toJSONString(loginResult, SerializerFeature.DisableCircularReferenceDetect);
- //获取输出流
- ServletOutputStream outputStream = response.getOutputStream();
- outputStream.write(result.getBytes(StandardCharsets.UTF_8));
- outputStream.flush();
- outputStream.close();
-
- }
- }
- package com.manong.config.security.handler;
-
- import com.alibaba.fastjson.JSON;
- import com.alibaba.fastjson.serializer.SerializerFeature;
- import com.baomidou.mybatisplus.extension.api.R;
- import com.manong.entity.User;
- import com.manong.utils.Result;
- import com.manong.utils.ResultCode;
- import org.springframework.security.authentication.*;
- 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.ServletOutputStream;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import java.io.IOException;
- import java.nio.charset.StandardCharsets;
-
- @Component
- public class LoginFailureHandler implements AuthenticationFailureHandler {
-
- @Override
- public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
- //设置相应编码格式
- response.setContentType("application/json;charset-utf-8");
- //获取输出流
- ServletOutputStream outputStream = response.getOutputStream();
- //定义变量,保存异常信息
- String message=null;
- //判断异常类型
- if(exception instanceof AccountExpiredException){
- message="账户过期失败";
- }
- else if(exception instanceof BadCredentialsException){
- message="用户名的账号密码错误,登录失败";
- }
- else if(exception instanceof CredentialsExpiredException){
- message="密码过期,登录失败";
- }
- else if(exception instanceof DisabledException){
- message="账号过期,登录失败";
- }
- else if(exception instanceof LockedException){
- message="账号被上锁,登录失败";
- }
- else if(exception instanceof InternalAuthenticationServiceException){
- message="用户不存在";
- }
-
- else {
- message="登录失败";
- }
- //将结果转换为Json格式
- String result = JSON.toJSONString(Result.error().code(ResultCode.ERROR).message(message));
- //将结果保存到输出中
- outputStream.write(result.getBytes(StandardCharsets.UTF_8));
- outputStream.flush();
- outputStream.close();
-
-
- }
- }
- package com.manong.config.security.handler;
-
-
- import com.alibaba.fastjson.serializer.SerializerFeature;
- import com.manong.entity.User;
- import com.manong.utils.Result;
- import com.manong.utils.ResultCode;
- import org.springframework.security.access.AccessDeniedException;
- import org.springframework.security.core.Authentication;
- import org.springframework.security.core.AuthenticationException;
- import org.springframework.security.web.AuthenticationEntryPoint;
- import org.springframework.security.web.access.AccessDeniedHandler;
- import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
- import org.springframework.stereotype.Component;
- import sun.net.www.protocol.http.AuthenticationHeader;
-
- import javax.servlet.ServletException;
- import javax.servlet.ServletOutputStream;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import java.io.IOException;
- import java.nio.charset.StandardCharsets;
-
- import com.alibaba.fastjson.JSON;
-
- /*
- * 匿名访问无权限资源处理器
- * */
- @Component
- public class AnonymousAuthenticationHandler implements AuthenticationEntryPoint {
-
- @Override
- public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
- response.setContentType("application/json;charset-utf-8");
- //获取输出流
- ServletOutputStream outputStream = response.getOutputStream();
- //将对象转换成json格式
- //消除循环引用
- String result = JSON.toJSONString(Result.error().code(ResultCode.NO_AUTH).message("匿名用户无权限访问"), SerializerFeature.DisableCircularReferenceDetect);
- //获取输出流
- outputStream.write(result.getBytes(StandardCharsets.UTF_8));
- outputStream.flush();
- outputStream.close();
- }
- }
- package com.manong.config.security.handler;
-
-
- import com.alibaba.fastjson.serializer.SerializerFeature;
- import com.manong.entity.User;
- import com.manong.utils.Result;
- import com.manong.utils.ResultCode;
- import org.springframework.security.access.AccessDeniedException;
- import org.springframework.security.core.Authentication;
- import org.springframework.security.web.access.AccessDeniedHandler;
- import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
- import org.springframework.stereotype.Component;
- import sun.net.www.protocol.http.AuthenticationHeader;
-
- import javax.servlet.ServletException;
- import javax.servlet.ServletOutputStream;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import java.io.IOException;
- import java.nio.charset.StandardCharsets;
-
- import com.alibaba.fastjson.JSON;
-
- /*
- * 认证用户访问无权限资源处理器
- * */
- @Component
- public class CustomerAccessDeniedHandler implements AccessDeniedHandler {
- @Override
- public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
- response.setContentType("application/json;charset-utf-8");
- ServletOutputStream outputStream = response.getOutputStream();
- //将对象转换成json格式
- //消除循环引用
- String result = JSON.toJSONString(Result.error().code(ResultCode.NO_AUTH).message("用户无权限访问,请联系教务处"), SerializerFeature.DisableCircularReferenceDetect);
- //获取输出流
- outputStream.write(result.getBytes(StandardCharsets.UTF_8));
- outputStream.flush();
- outputStream.close();
- }
- }
- package com.manong.config.security.service;
-
- import com.manong.config.security.handler.AnonymousAuthenticationHandler;
- import com.manong.config.security.handler.CustomerAccessDeniedHandler;
- import com.manong.config.security.handler.LoginFailureHandler;
- import com.manong.config.security.handler.LoginSuccessHandler;
- 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.config.http.SessionCreationPolicy;
- import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
- import org.springframework.stereotype.Component;
-
- import javax.annotation.Resource;
-
- @Component
- @EnableWebSecurity
- public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
-
- @Resource
- private LoginSuccessHandler loginSuccessHandler;
- @Resource
- private LoginFailureHandler loginFailureHandler;
- @Resource
- private CustomerAccessDeniedHandler customerAccessDeniedHandler;
- @Resource
- private AnonymousAuthenticationHandler anonymousAuthenticationHandler;
- @Resource
- private CustomerUserDetailService customerUserDetailService;
-
- //注入加密类
- @Bean
- public BCryptPasswordEncoder passwordEncoder(){
- return new BCryptPasswordEncoder();
- }
- //处理登录认证
- @Override
- protected void configure(HttpSecurity http) throws Exception {
- //登录过程处理
- http.formLogin() //表单登录
- .loginProcessingUrl("/api/user/login") //登录请求url地址
- .successHandler(loginSuccessHandler) //认证成功
- .failureHandler(loginFailureHandler) //认证失败
- .and()
- .csrf().disable()
- .sessionManagement()
- .sessionCreationPolicy(SessionCreationPolicy.STATELESS) //不创建Session
- .and().authorizeRequests() //设置需要拦截的请求
- .antMatchers("/api/user/login").permitAll()//登录放行
- .anyRequest().authenticated() //其他请求一律拦截
- .and()
- .exceptionHandling()
- .authenticationEntryPoint(anonymousAuthenticationHandler) //匿名无权限类
- .accessDeniedHandler(customerAccessDeniedHandler) //认证用户无权限
- .and()
- .cors();//支持跨域
- }
-
-
- //认证配置处理器
- @Override
- protected void configure(AuthenticationManagerBuilder auth) throws Exception {
- auth.userDetailsService(customerUserDetailService)
- .passwordEncoder(this.passwordEncoder());//密码加密
- }
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。