目录
1、Spring Security介绍
2、Spring Security认证步骤
3、Spring Security认证实现
3.1 添加Spring Security依赖
3.2 自定义UserDetails类
3.3 编写UserService接口
3.4 编写UserService接口实现类
3.5 自定义UserDetailsService类
3.6 编写自定义认证成功处理器
3.7 编写自定义认证失败处理器
3.8 编写用户无权限访问处理器
3.9 编写匿名用户访问资源处理器
3.10 编写Spring Security配置类
@Getter
@Setter
@TableName("sys_user")
@ApiModel(value = "User对象", description = "")
public class User implements Serializable, UserDetails {
private static final long serialVersionUID = 1L;
@ApiModelProperty("用户编号")
@TableId(value = "id", type = IdType.AUTO)
private Long id;
@ApiModelProperty("登录名称(用户名)")
private String username;
@ApiModelProperty("登录密码")
private String password;
@ApiModelProperty("帐户是否过期(1-未过期,0-已过期)")
private boolean isAccountNonExpired =true;
@ApiModelProperty("帐户是否被锁定(1-未过期,0-已过期)")
private boolean isAccountNonLocked=true;
@ApiModelProperty("密码是否过期(1-未过期,0-已过期)")
private boolean isCredentialsNonExpired=true;
@ApiModelProperty("帐户是否可用(1-可用,0-禁用)")
private boolean isEnabled =true;
@ApiModelProperty("真实姓名")
private String realName;
@ApiModelProperty("昵称")
private String nickName;
@ApiModelProperty("所属部门ID")
private Long departmentId;
@ApiModelProperty("所属部门名称")
private String departmentName;
@ApiModelProperty("性别(0-男,1-女)")
private Integer gender;
@ApiModelProperty("电话")
private String phone;
@ApiModelProperty("邮箱")
private String email;
@ApiModelProperty("用户头像")
private String avatar;
@ApiModelProperty("是否是管理员(1-管理员)")
private Integer isAdmin;
@ApiModelProperty("创建时间")
private Date createTime;
@ApiModelProperty("修改时间")
private Date updateTime;
@ApiModelProperty("是否删除(0-未删除,1-已删除)")
private Integer isDelete;
/** * 权限列表 */
@TableField(exist = false)
Collection extends GrantedAuthority> authorities;
@TableField(exist = false)
private List permissionList;
}
在com.cizhu.service.UserService接口编写根据用户名查询用户信息的方法
public interface IUserService extends IService {
/**
* 根据用户名查询用户信息
* @param userName
* @return
*/
User findUserByUserName(String userName);
}
@Service
@Transactional
public class UserServiceImpl extends ServiceImpl implements IUserService {
/**
* 根据用户名查询用户信息
* @param userName
* @return
*/
@Override
public User findUserByUserName(String userName) {
QueryWrapper queryWrapper = new QueryWrapper<>();
queryWrapper.eq("username",userName);
// 返回查询记录
return baseMapper.selectOne(queryWrapper);
}
}
@Component
public class CustomerUserDetailsService implements UserDetailsService {
@Resource
private IUserService userService;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
//调用根据用户名查询用户信息的方法
User user = userService.findUserByUserName(username);
if (user ==null){
throw new UsernameNotFoundException("用户名或密码错误");
}
return user;
}
}
在com.cizhu.config.security.handler包下创建LoginSuccessHandler登录认证成功处理器
@Component
public class LoginSuccessHandler implements AuthenticationSuccessHandler {
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
//设置响应编码格式
response.setContentType("applicaiton/json;charset=utf-8");
//换取当前登录用户信息
User user = (User)authentication.getPrincipal();
//将对象转换成JSON格式,并消除循环引用
String result = JSON.toJSONString(user, SerializerFeature.DisableCircularReferenceDetect);
//获取输出流
ServletOutputStream outputStream = response.getOutputStream();
outputStream.write(result.getBytes(StandardCharsets.UTF_8));
outputStream.flush();
outputStream.close();
}
}
@Component
public class LoginFailureHandler implements AuthenticationFailureHandler {
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
//设置响应编码格式
response.setContentType("applicaiton/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(500).message(message));
//将结果保存输出中写出
outputStream.write(result.getBytes(StandardCharsets.UTF_8));
outputStream.flush();
outputStream.close();
}
在com.cizhu.config.security.handler包下创建CustomerAccessDeniedHandler认证用户访问无权限资源时处理器类。
package com.cizhu.config.security.service.handler;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.cizhu.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.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
/**
* 用户无权限访问资源处理器
*/
@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();
//消除循环引用
String result = JSON.toJSONString(Result.error().code(700).message(
"无权限访问, 请联系管理员!"), SerializerFeature.DisableCircularReferenceDetect);
outputStream.write(result.getBytes(StandardCharsets.UTF_8));
outputStream.flush();
outputStream.close();
}
}
在com.cizhu.config.security.handler包下创建AnonymousAuthenticationHandler匿名用户访问资源处理器类
package com.cizhu.config.security.service.handler;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.cizhu.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.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
/**
* 匿名用户访问资源处理器
*/
@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-");
//获取输出流
ServletOutputStream outputStream = response.getOutputStream();
//消除循环引用
String result = JSON.toJSONString(Result.error().code(600).message(
"匿名用户无权限访问!"), SerializerFeature.DisableCircularReferenceDetect);
outputStream.write(result.getBytes(StandardCharsets.UTF_8));
outputStream.flush();
outputStream.close();
}
}
在com.cizhu.config.security包下创建SpringSecurityConfig配置类
package com.cizhu.config.security;
import com.cizhu.config.security.filter.CheckTokenFilter;
import com.cizhu.config.security.handler.AnonymousAuthenticationHandler;
import com.cizhu.config.security.handler.CustomerAccessDeniedHandler;
import com.cizhu.config.security.handler.LoginFailureHandler;
import com.cizhu.config.security.handler.LoginSuccessHandler;
import com.cizhu.config.security.service.CustomerUserDetailsService;
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.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import javax.annotation.Resource;
@Configuration
@EnableWebSecurity
//开启权限注解控制
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {
@Resource
private CustomerUserDetailsService customerUserDetailsService;
@Resource
private LoginSuccessHandler loginSuccessHandler;
@Resource
private LoginFailureHandler loginFailureHandler;
@Resource
private AnonymousAuthenticationHandler anonymousAuthenticationHandler;
@Resource
private CustomerAccessDeniedHandler customerAccessDeniedHandler;
@Resource
private CheckTokenFilter checkTokenFilter;
/**
*
* .. 测试登录认证接口
* 注入加密处理类
*
* @return
*/
@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
//登录前进行过滤
http.addFilterBefore(checkTokenFilter,
UsernamePasswordAuthenticationFilter.class);
http.formLogin()
.loginProcessingUrl("/api/user/login")
// 设置登录验证成功或失败后的的跳转地址
.successHandler(loginSuccessHandler).failureHandler(loginFailureHandler)
// 禁用csrf防御机制
.and().csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/api/user/login").permitAll()
.anyRequest().authenticated()
.and()
.exceptionHandling()
.authenticationEntryPoint(anonymousAuthenticationHandler)
.accessDeniedHandler(customerAccessDeniedHandler)
.and().cors();//开启跨域配置
}
/**
* 配置认证处理器
*
* @param auth
* @throws Exception
*/
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(customerUserDetailsService).passwordEncoder(
passwordEncoder());
}
}