适用于前后端分离的SpringSecurity框架
配置类:
package com.example.demo3.config;
import com.example.demo3.config.Handler.RestAuthorizationAccessDeniedHandler;
import com.example.demo3.service.impl.MyUserDetailsServiceImpl;
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.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
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.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
/**
* @author 无敌暴龙神
* 注解表示开启安全框架,开始过滤所有的url;同时标志这个类是一个配置类
*/
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
/**
* 自己定义的处理器;;;可能是错的,因为我忘了这个是干啥的
*/
private RestAuthorizationAccessDeniedHandler accessDeniedHandler;
/**
* 将自定义的登录成功处理器注入进来
*/
@Autowired
private AuthenticationSuccessHandler authenticationSuccessHandler;
/**
* 将自定义的登录失败处理器注入进来
*/
@Autowired
private AuthenticationFailureHandler authenticationFailureHandler;
/**
* 配置http请求验证等
*
* @param http
* @throws Exception
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
// 给一个异常处理器,走我们自定义的拒绝访问处理器
http.exceptionHandling().accessDeniedHandler(accessDeniedHandler);
//给定处理器;登录成功的处理器和登录失败的处理器
http.formLogin().
successHandler(authenticationSuccessHandler).
failureHandler(authenticationFailureHandler);
// 所有的请求都需要登录才能进行
http.authorizeRequests()
.anyRequest().authenticated();
}
/**
* 创建密码加密器
*/
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
异常处理器:
package com.example.demo3.config.Handler;
import com.fasterxml.jackson.databind.ObjectMapper;
import jdk.nashorn.internal.ir.Assignment;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.web.access.AccessDeniedHandler;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;
/**
* @author 无敌暴龙神
* 处理器
*/
@Configuration
public class RestAuthorizationAccessDeniedHandler implements AccessDeniedHandler {
/**
*
* @param request
* @param response
* @param e
* @throws IOException
* @throws ServletException
*/
@Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException e) throws IOException, ServletException {
// 设置数据类型
response.setContentType("application/json;charset=utf-8");
// 封装数据
Map<String, Object> data = new HashMap<>(4);
//
data.put("code", "403");
data.put("msg", "您没有访问权限");
// 拿到响应流
PrintWriter writer = response.getWriter();
// 创建类型转换器
ObjectMapper objectMapper = new ObjectMapper();
String s = objectMapper.writeValueAsString(data);
// 将数据写出去
writer.write(s);
writer.flush();
writer.close();
}
}
登录成功的处理器
package com.example.demo3.config.Handler;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;
/**
* 登录成功的处理器
*/
@Configuration
public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException {
//设置返回数据格式
response.setContentType("application/json;charset=utf-8");
//获取输出流
PrintWriter pw = response.getWriter();
//定义集合
Map<String, Object> map = new HashMap<>();
//给集合添加参数
map.put("code", 200);
// 将用户信息放进去
map.put("msg", authentication.getPrincipal());
//把map写到前端
pw.write(new ObjectMapper().writeValueAsString(map));
pw.flush();
pw.close();
}
}
登录失败的处理器
package com.example.demo3.config.Handler;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.*;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;
@Configuration
public class MyAuthenticationFailureHandler implements AuthenticationFailureHandler {
@Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
//设置返回的数据格式
response.setContentType("application/json;charset=utf-8");
//获取数据流
PrintWriter pw = response.getWriter();
Map<String, Object> map = new HashMap<>();
map.put("code", 401);
//根据异常返回不同的msg
if (exception instanceof LockedException) {
map.put("msg", "账户被锁定,登陆失败!");
} else if (exception instanceof BadCredentialsException) {
map.put("msg", "账户或者密码错误,登陆失败!");
} else if (exception instanceof DisabledException) {
map.put("msg", "账户被禁用,登陆失败!");
} else if (exception instanceof AccountExpiredException) {
map.put("msg", "账户已过期,登陆失败!");
} else if (exception instanceof CredentialsExpiredException) {
map.put("msg", "密码已过期,登陆失败!");
} else {
map.put("msg", "登陆失败!");
}
pw.write(new ObjectMapper().writeValueAsString(map));
pw.flush();
pw.close();
}
}
service层
package com.example.demo3.service.impl;
import com.example.demo3.mapper.SysUserMapper;
import com.example.demo3.pojo.SysUser;
import org.springframework.beans.factory.annotation.Autowired;
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 org.springframework.util.Assert;
import java.util.List;
/**
* @author 无敌暴龙神
* 实现UserDetailsService接口
*/
@Service
public class MyUserDetailsServiceImpl implements UserDetailsService {
/**
* 注入mapper层的接口,在数据库中去查询对应的用户信息
*/
@Autowired
private SysUserMapper sysUserMapper;
@Override
public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {
//判断用户名是否为空
if (s==null){
return null;
}
//根据用户名去sql表中查询用户
SysUser sysUser=sysUserMapper.findUserByUserName(s);
//判断是否存在该用户
if (sysUser==null){
return null;
}
//根据用户的id去查询对应的权限合集
List<String> auths=sysUserMapper.findAuthByUserId(sysUser.getUserid());
//将查询得到的权限合集注入到实体类
sysUser.setAuths(auths);
//返回实体类
return sysUser;
}
}
实体类
package com.example.demo3.pojo;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
/**
* @author 无敌暴龙神
* 实现UserDetails接口,这样service层重写的方法可以直接返回该实体类
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class SysUser implements Serializable, UserDetails {
private Integer userid;
private String username;
private String userpwd;
private String sex;
private String address;
/**
* 用户状态字段(1:正常 0:禁用)
*/
private Integer status;
private static final long serialVersionUID = 1L;
/**
* 权限合集,由外部service层注入
*/
private List<String> auths=null;
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
//创建GrantedAuthority的list集合
List<GrantedAuthority> list=new ArrayList<>();
//循环查询得到的权限合集,将权限合集存入该list结合
this.auths.forEach(auth->{
list.add(new SimpleGrantedAuthority(auth));
});
return list;
}
/**
*
* @return 返回查询的用户名;这个方法与本实体类的get属性的方法方法名一致,会被idea默认覆盖
* 需要手动添加该getUsername放
*/
@Override
public String getUsername() {
return this.username;
}
/**
*
* @return 返回用用户名查询到的密码,返回
*/
@Override
public String getPassword() {
return this.userpwd;
}
/**
* 判断用户状态====过期
* @return
*/
@Override
public boolean isAccountNonExpired() {
return this.status==1;
}
/**
* 判断用户状态====锁定
* @return
*/
@Override
public boolean isAccountNonLocked() {
return this.status==1;
}
/**
* 判断用户状态====凭证过期
* @return
*/
@Override
public boolean isCredentialsNonExpired() {
return this.status==1;
}
/**
* 判断用户状态====禁用
* @return
*/
@Override
public boolean isEnabled() {
return this.status==1;
}
}
测试后发现,当我service层实现了
UserDetailsService接口后,重写了loadUserByUsername方法;框架会默认直接去访问自己写的servic层去获取用户住处;而不是去内存中获取用户住处