Security 项目案例
1.自定义Spring Security 核心配置类
TokenWebSecurityConfig.class
package com.wechat.web.conf.security;
import com.wechat.web.persistence.service.IAdminUserService;
import com.wechat.web.persistence.service.IWxUserService;
import org.springframework.beans.factory.annotation.Autowired;
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.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
/**
* secutiry 核心配置类
* @program: webSite
* @description
* @author: Joker
* @create: 2021-06-25 16:59
**/
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(securedEnabled = true)
public class TokenWebSecurityConfig extends WebSecurityConfigurerAdapter {
private TokenManager tokenManager;
// private RedisTemplate redisTemplate;
private DefaultPasswordEncoder defaultPasswordEncoder;
private UserDetailsService userDetailsService;
private TokenLoginFilter tokenLoginFilter;
private IWxUserService wxUserService;
private IAdminUserService adminUserService;
@Autowired
public TokenWebSecurityConfig(DefaultPasswordEncoder defaultPasswordEncoder, TokenManager tokenManager, UserDetailsService userDetailsService,IWxUserService wxUserService,IAdminUserService adminUserService){
this.defaultPasswordEncoder = defaultPasswordEncoder;
this.tokenManager = tokenManager;
// this.redisTemplate = redisTemplate;
this.userDetailsService = userDetailsService;
this.wxUserService = wxUserService;
this.adminUserService = adminUserService;
this.tokenLoginFilter = new TokenLoginFilter(tokenManager,wxUserService,adminUserService);
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.
// 任何尚未匹配的URL都需要对用户进行身份验证
formLogin().loginProcessingUrl("/wx-user/login")
//退出路径
.and().logout().logoutUrl("/wx-user/logOut")
.addLogoutHandler(new TokenLogoutHandler(tokenManager))
// .exceptionHandling()
.and().cors().and().csrf().disable()
// .authorizeRequests()
// 定义了默认允许访问的资源(不需要认证)
.exceptionHandling()
//没有权限访问
.accessDeniedHandler(new AccessDeniedAuthenticationHandler())
.authenticationEntryPoint(new UnauthEntryPoint())
// authenticationManager()
//认证过滤器
.and()
.addFilter(new TokenLoginFilter(tokenManager,wxUserService,adminUserService))
//授权过滤器
.addFilter(new TokenAuthFilter(authenticationManager(),tokenManager))
.authorizeRequests()
.antMatchers(
).permitAll()
.anyRequest().authenticated()
.and()
.httpBasic();
}
//调用userDateilsService和密码处理
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
System.out.println("处理");
auth.authenticationProvider(tokenLoginFilter);
// userDetailsService(userDetailsService)
// .passwordEncoder(defaultPasswordEncoder);
}
//不进行认证的路径,可以直接进行访问
@Override
public void configure(WebSecurity web) throws Exception {
web.ignoring().antMatchers("/api/**",
// "/**",
"/swagger-**/**",
"favicon.ico",
"/v2/api-docs",
"/webjars/**",
"/error",
"/wx-user/wxLogin",
"/wx-user/callBack",
"/wx-user/reply",
"/web/v1/**"
// "/MP_verify_kAZg9REXJwINtdRC.txt"
);
}
}
2.自定义登录认证过滤器
TokenLoginFilter.class
package com.wechat.web.conf.security;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.wechat.web.persistence.domain.AdminUserBean;
import com.wechat.web.persistence.domain.WxUserBean;
import com.wechat.web.persistence.service.IAdminUserService;
import com.wechat.web.persistence.service.IWxUserService;
import com.wechat.web.tool.beanUtil.BeanMapper;
import com.wechat.web.tool.enums.Msg;
import com.wechat.web.tool.result.Result;
import com.wechat.web.tool.unifyUser.SecurityUser;
import com.wechat.web.tool.unifyUser.ServletUtils;
import com.wechat.web.tool.unifyUser.UnifyUser;
import com.wechat.web.persistence.vo.login.LoginVO;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
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 org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import javax.servlet.FilterChain;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
/**
* 登录认证过滤器
* @program: webSite
* @description
* @author: Joker
* @create: 2021-06-25 16:20
**/
public class TokenLoginFilter extends UsernamePasswordAuthenticationFilter implements AuthenticationProvider {
private TokenManager tokenManager;
// private RedisTemplate redisTemplate;
private AuthenticationManager authenticationManager;
private IWxUserService wxUserService;
private IAdminUserService adminUserService;
public TokenLoginFilter(TokenManager tokenManager,IWxUserService wxUserService,IAdminUserService adminUserService){
// this.authenticationManager = authenticationManager;
this.tokenManager = tokenManager;
this.wxUserService = wxUserService;
this.adminUserService = adminUserService;
// this.redisTemplate = redisTemplate;
this.setPostOnly(false);
this.setRequiresAuthenticationRequestMatcher(new AntPathRequestMatcher("/wx-user/login","POST"));
}
/***
* 授权 及 认证 处理方法
* @param authentication
* @return
* @throws AuthenticationException
*/
@Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
//1.获取放入的登录信息
WxLoginAuthenticationToken token = (WxLoginAuthenticationToken)authentication;
Object principal = token.getPrincipal();
LoginVO loginVO = (LoginVO)principal;
System.out.println("登录授权信息对象:"+loginVO.toString());
// type == 1 微信授权登录
if(loginVO.getType()==1){
//2.根据openId查询数据库用户
WxUserBean serviceOpenId = wxUserService.getOpenId(loginVO.getOpenId());
//3. 数据库未查询到用户信息则为注册
if(serviceOpenId==null){
serviceOpenId = wxUserService.saveWxUser(loginVO);
}
// 4. 返回登录授权处理信息
return new WxLoginAuthenticationToken(serviceOpenId,loginVO.getType());
}else { // 后台管理登录
System.out.println(loginVO.getAccount());
System.out.println(loginVO.getPassword());
// 根据账号密码查询管理员
AdminUserBean adminUserBean = adminUserService.getAccount(loginVO.getAccount(), loginVO.getPassword());
if(adminUserBean!=null){
// 4. 返回管理员信息
return new WxLoginAuthenticationToken(adminUserBean,loginVO.getType());
}
//用户名或密码错误
return new WxLoginAuthenticationToken("用户名或者密码错误",3);
}
}
@Override
public boolean supports(Class<?> aClass) {
return true;
}
//1.获取表单提交的用户名和密码
@Override
public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response)
throws AuthenticationException {
try {
//1.获取表单数据对象
LoginVO Login = new ObjectMapper().readValue(request.getInputStream(), LoginVO.class);
System.out.println("表单提交的信息:"+Login.toString());
//2.将数据交给自定义登录授权 WxLoginAuthenticationToken
return this.authenticate(new WxLoginAuthenticationToken(Login,Login.getType()));
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException();
}
}
//2.认证成功调用的方法
@Override
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult){
//1.获取登录认证成功后的wxUserBean信息
System.out.println("登录认证成功wxUser:"+authResult.getPrincipal().toString());
WxLoginAuthenticationToken wxLoginAuthenticationToken = (WxLoginAuthenticationToken)authResult;
Object principal = wxLoginAuthenticationToken.getPrincipal();
Integer type = wxLoginAuthenticationToken.getType();
//2.将wxUserBean 转换成 UnifyUser
UnifyUser unifyUser = null;
if(type==1){
WxUserBean wxUserBean = (WxUserBean)principal;
unifyUser = BeanMapper.map(wxUserBean, UnifyUser.class);
}else if (type==2){
AdminUserBean adminUserBean = (AdminUserBean)principal;
unifyUser = BeanMapper.map(adminUserBean, UnifyUser.class);
}else {
ServletUtils.responseJson(response, Result.error(Msg.E40002));
return;
}
// SecurityUser user = new SecurityUser();
// user.setCurrentUserInfo(wxUserBean);
// UnifyUser unifyUser = BeanMapper.map(user.getCurrentUserInfo(), UnifyUser.class);
// unifyUser.setRoleCodes(user.getPermissionValueList());
System.out.println("登录认证成功unifyUser:"+unifyUser.toString());
//3.将unifyUser 对象转换成JsonString
String jsonString = JSONObject.toJSONString(unifyUser);
//4.根据认证成功用户信息json字符串生成token
String token = tokenManager.createToken(jsonString);
//5.将token存入响应返回头
response.setHeader("token",token);
//6.将token放入unifyUser对象
unifyUser.setToken(token);
ServletUtils.responseJson(response, Result.success(unifyUser));
}
//3.认证失败调用的方法
@Override
protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed){
//1.返回 登陆失败、用户名或密码错误
System.out.println("认证失败调用的方法");
ServletUtils.responseJson(response, Result.error(Msg.E40002));
}
}
3.自定义请求认证授权过滤器
TokenAuthFilter.class
package com.wechat.web.conf.security;
import com.alibaba.fastjson.JSONObject;
import com.wechat.web.tool.enums.Msg;
import com.wechat.web.tool.result.Result;
import com.wechat.web.tool.unifyUser.ServletUtils;
import com.wechat.web.tool.unifyUser.UnifyUser;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
* 请求认证相关 授权过滤
*
* @program: webSite
* @description
* @author: Joker
* @create: 2021-06-25 16:42
**/
public class TokenAuthFilter extends BasicAuthenticationFilter {
private TokenManager tokenManager;
// private RedisTemplate redisTemplate;
public TokenAuthFilter(AuthenticationManager authenticationManager,TokenManager tokenManager) {
super(authenticationManager);
this.tokenManager = tokenManager;
// this.redisTemplate = redisTemplate;
}
//1.接收请求、进行token认证
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
//1.获取当前认证成功用户权限信息
UsernamePasswordAuthenticationToken authRequest = getAuthentication(request,response);
System.out.println(authRequest);
//2.判断如果有权限信息,放到权限上下文中
if(authRequest!=null){
//3.存储当前用户对象
SecurityContextHolder.getContext().setAuthentication(authRequest);
//4.放行
chain.doFilter(request,response);
}else{
//5.未获取到用户信息
ServletUtils.responseJson(response, Result.error(Msg.F00001));
}
}
// 2.获取当前认证成功用户权限信息
private UsernamePasswordAuthenticationToken getAuthentication(HttpServletRequest request, HttpServletResponse response){
System.out.println("进入请求认证");
//1.从请求头head中获取token
String token = request.getHeader("token");
System.out.println("从请求头head中获取token:"+token);
if(token!=null){
//2.根据token字符串得到用户信息
String userInfo = tokenManager.getUserInfoFromToken(response,token);
//3.token解析后为空则返回null
if(userInfo==null){
return null;
}
System.out.println("token获取到的用户信息:"+userInfo);
//3.JWT 解析用户信息string转换成UnifyUser对象
UnifyUser users = JSONObject.parseObject(userInfo, UnifyUser.class);
users.setToken(token);
//4.将UnifyUser 中 权限列表 添加到 authorities中
List<String> permissionValueList = users.getRoleCodes();
Collection<GrantedAuthority> authorities = new ArrayList<>();
if(permissionValueList!=null){
for (String permissionValue:permissionValueList){
SimpleGrantedAuthority auth = new SimpleGrantedAuthority(permissionValue);
authorities.add(auth);
}
}
System.out.println("UnifyUser内容:"+users.toString());
return new UsernamePasswordAuthenticationToken(users,token,authorities);
}
// 5.请求中未包含token信息、返回前端请登录
ServletUtils.responseJson(response, Result.error(Msg.E40001));
return null;
}
}
4.自定义退出登录处理器
TokenLogoutHandler.class
package com.wechat.web.conf.security;
import com.wechat.web.tool.enums.Msg;
import com.wechat.web.tool.result.Result;
import com.wechat.web.tool.unifyUser.ServletUtils;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.logout.LogoutHandler;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 退出登录处理器
*
* @program: webSite
* @description
* @author: Joker
* @create: 2021-06-25 16:04
**/
public class TokenLogoutHandler implements LogoutHandler {
private TokenManager tokenManager;
// private RedisTemplate redisTemplate;
public TokenLogoutHandler(TokenManager tokenManager){
this.tokenManager = tokenManager;
// this.redisTemplate = redisTemplate;
}
@Override
public void logout(HttpServletRequest request, HttpServletResponse httpServletResponse, Authentication authentication) {
//1.从请求头hread里获取token
String token = request.getHeader("token");
System.out.println("退出处理获取的hread中token:"+token);
// 2.token不为空
if(token!=null){
// 2.1从token中获取用户基本信息
String userJson = tokenManager.getUserInfoFromToken(httpServletResponse,token);
if(userJson==null){
// 2.2 登陆信息已过期,请重新登陆!
ServletUtils.responseJson(httpServletResponse, Result.error(Msg.F10006));
}
// 2.3 调用JWT方法、移除token信息
tokenManager.removeToken(token);
// 3.注销成功 token信息已删除
ServletUtils.responseJson(httpServletResponse, Result.error(Msg.F10003));
}else {
// 4.注销失败! 未在请求头中获取到token
ServletUtils.responseJson(httpServletResponse, Result.error(Msg.F10004));
}
}
}
5.自定义权限验证过滤器
UnauthEntryPoint.class
package com.wechat.web.conf.security;
import com.wechat.web.tool.enums.Msg;
import com.wechat.web.tool.result.Result;
import com.wechat.web.tool.unifyUser.ServletUtils;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 权限验证过滤器 AuthenticationEntryPoint 该类用来统一处理 AuthenticationException 异常
* @program: webSite
* @description
* @author: Joker
* @create: 2021-06-25 17:09
**/
public class UnauthEntryPoint implements AuthenticationEntryPoint {
//1.无权限访问
@Override
public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e){
// 返回信息 访问失败,无权限进行访问!
System.out.println("访问失败,无权限进行访问!");
ServletUtils.responseJson(httpServletResponse, Result.error(Msg.F10005));
}
}
6.自定义异常处过滤器
AccessDeniedAuthenticationHandler.class
package com.wechat.web.conf.security;
import com.wechat.web.tool.enums.Msg;
import com.wechat.web.tool.result.Result;
import com.wechat.web.tool.unifyUser.ServletUtils;
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;
/**
*
* AccessDeniedHandler 该类用来统一处理 AccessDeniedException 异常
* @program: webSite
* @description
* @author: Joker
* @create: 2021-06-30 10:06
**/
public class AccessDeniedAuthenticationHandler implements AccessDeniedHandler {
public AccessDeniedAuthenticationHandler() {
}
@Override
public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException {
System.out.println("无权限访问");
ServletUtils.responseJson(httpServletResponse, Result.error(Msg.F10005));
}
}
7.自定义微信授权登录
WxLoginAuthenticationToken.class
package com.wechat.web.conf.security;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import java.util.Collection;
/**
* 自定义登录授权 WxLoginAuthenticationToken
* @program: webSite
* @description
* @author: Joker
* @create: 2021-06-25 16:20
**/
public class WxLoginAuthenticationToken extends AbstractAuthenticationToken {
private static final long serialVersionUID = 510L;
private final Object principal;
private Object credentials;
// 1:微信公众号授权登录 2:后台管理界面登录
private Integer type;
public WxLoginAuthenticationToken(Object principal,Integer type) {
super((Collection)null);
this.principal = principal;
this.type = type;
// this.credentials = credentials;
this.setAuthenticated(false);
}
public WxLoginAuthenticationToken(Object principal, Object credentials) {
super((Collection)null);
this.principal = principal;
this.credentials = credentials;
this.setAuthenticated(false);
}
public WxLoginAuthenticationToken(Object principal, Object credentials, Collection<? extends GrantedAuthority> authorities) {
super(authorities);
this.principal = principal;
this.credentials = credentials;
super.setAuthenticated(true);
}
public Object getCredentials() {
return this.credentials;
}
public Object getPrincipal() {
return this.principal;
}
public Integer getType() {
return type;
}
public void setType(Integer type) {
this.type = type;
}
public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
if (isAuthenticated) {
throw new IllegalArgumentException("Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead");
} else {
super.setAuthenticated(false);
}
}
public void eraseCredentials() {
super.eraseCredentials();
this.credentials = null;
}
@Override
public String toString() {
return "WxLoginAuthenticationToken{" +
"principal=" + principal +
", credentials=" + credentials +
", type=" + type +
'}';
}
}
|