SpringBoot+SpringSecurity+Jwt 完整实战源码

验证流程

登录 -> security 登录验证 -> 验证通过,返回Token给前端 , Token包含登录用户所拥有的所有权限

前端每次请求接口时,携带Token -> security 拦截器解析Token,并判断有没有权限访问这个接口

用户角色权限表设计

用户表

CREATE TABLE `admin` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_email` varchar(64) DEFAULT NULL COMMENT '用户邮箱',
  `password` varchar(50) DEFAULT NULL COMMENT '密码',
  `user_name` varchar(20) DEFAULT NULL COMMENT '用户名',
  `mobile` varchar(20) DEFAULT NULL COMMENT '手机',
  `department` varchar(64) DEFAULT NULL COMMENT '部门',
  `browser` varchar(64) DEFAULT NULL COMMENT '使用浏览器',
  `address` varchar(250) DEFAULT NULL COMMENT '地址',
  `updater` int(11) DEFAULT NULL COMMENT '更改人',
  `last_login` datetime DEFAULT NULL COMMENT '最后登录时间',
  `update_time` datetime DEFAULT NULL COMMENT '更新时间',
  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  `pwd` varchar(11) DEFAULT NULL,
  `create_user` int(11) DEFAULT NULL COMMENT '添加人',
  `status` int(11) NOT NULL DEFAULT '1' COMMENT '状态 1:使用 2:禁止',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=9 DEFAULT CHARSET=utf8;

用户角色关系表

CREATE TABLE `admin_role` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `admin_id` int(11) NOT NULL COMMENT '管理员id',
  `role_id` int(11) NOT NULL COMMENT '权限id',
  `ctime` datetime DEFAULT NULL COMMENT '创建时间',
  `dtime` datetime DEFAULT NULL COMMENT '修改时间',
  `cuser` int(11) DEFAULT NULL COMMENT '创建人',
  `duser` int(11) DEFAULT NULL COMMENT '修改人',
  `status` int(1) DEFAULT NULL COMMENT '有没有效(1:有效;0:无效)',
  PRIMARY KEY (`id`),
  KEY `ADMIN_ROLE_ADMIN_ID` (`admin_id`) USING BTREE,
  KEY `ADMIN_ROLE_ROLE_ID` (`role_id`),
  CONSTRAINT `FK_ADMIN_ROLE_ROLE_ID` FOREIGN KEY (`role_id`) REFERENCES `role` (`ID`)
) ENGINE=InnoDB AUTO_INCREMENT=22 DEFAULT CHARSET=utf8;

权限表

CREATE TABLE `permission` (
  `ID` bigint(20) NOT NULL AUTO_INCREMENT,
  `parent_id` bigint(20) DEFAULT NULL COMMENT '父权限',
  `name` varchar(64) DEFAULT NULL COMMENT '权限名称',
  `enname` varchar(64) NOT NULL COMMENT '权限英文名称',
  `url` varchar(100) DEFAULT NULL COMMENT '授权路径',
  `type` char(1) NOT NULL COMMENT '类目:0菜单1操作',
  `description` longtext COMMENT '备注',
  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  `update_time` datetime DEFAULT NULL COMMENT '更新时间',
  `del_status` char(1) DEFAULT '0' COMMENT '逻辑删除:0保留1删除',
  PRIMARY KEY (`ID`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=103 DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT COMMENT='菜单权限表';

权限角色关系表

CREATE TABLE `role_permission` (
  `ID` int(11) NOT NULL AUTO_INCREMENT,
  `role_id` int(11) DEFAULT NULL COMMENT '角色id',
  `permission_id` int(11) DEFAULT NULL COMMENT '权限id',
  `ctime` datetime DEFAULT NULL COMMENT '创建时间',
  `dtime` datetime DEFAULT NULL COMMENT '更新时间',
  `cuser` int(11) DEFAULT NULL COMMENT '创建者id',
  `duser` int(11) DEFAULT NULL COMMENT '更新者id',
  `status` tinyint(3) DEFAULT NULL COMMENT '有没有效',
  PRIMARY KEY (`ID`) USING BTREE,
  KEY `FK_Reference_27` (`role_id`) USING BTREE,
  KEY `FK_Reference_28` (`permission_id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=733 DEFAULT CHARSET=utf8 ROW_FORMAT=COMPACT COMMENT='角色和权限的对应表';

SpringSecurity 实战

目录结构:

JWTAuthenticationTokenFilter 

import com.alibaba.fastjson.JSONObject;
import com.wuyuan.cs.manage.component.security.utils.JWTTokenUtil;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import lombok.extern.slf4j.Slf4j;
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 org.springframework.util.StringUtils;

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.List;
import java.util.Map;

/**
 * JWT接口请求校验拦截器
 * 请求接口时会进入这里验证Token是否合法和过期,如果令牌合法则获取用户信息,并且存入SecurityContextHolder
 */
@Slf4j
public class JWTAuthenticationTokenFilter extends BasicAuthenticationFilter {


    public JWTAuthenticationTokenFilter(AuthenticationManager authenticationManager) {
        super(authenticationManager);
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        // 获取请求头中JWT的Token
        String tokenHeader = request.getHeader(JWTTokenUtil.HEAD_KEY);
        if (null != tokenHeader && tokenHeader.startsWith(JWTTokenUtil.TOKEN_PREFIX)) {
            try {
                // 截取JWT前缀
                String token = tokenHeader.replace(JWTTokenUtil.TOKEN_PREFIX, "");
                // 解析JWT
                Claims claims = JWTTokenUtil.parseJWT(token);
                // 获取用户名
                String username = claims.getSubject();
                String password = claims.getId();
                if (!StringUtils.isEmpty(username) && !StringUtils.isEmpty(password)) {
                    // 获取权限
                    List<GrantedAuthority> authorities = new ArrayList<>();
                    String authority = claims.get("authorities").toString();
                    System.out.println(authority);
                    if (!StringUtils.isEmpty(authority)) {
                        List<Map<String, String>> authorityMap = JSONObject.parseObject(authority, List.class);
                        for (Map<String, String> auth : authorityMap) {
                            if (null != auth) {
                                authorities.add(new SimpleGrantedAuthority(auth.get("authority")));
                            }
                        }
                    }
                    // 从token中拿到登录的用户信息和权限 放到security的上下文中
                    UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(username, password, authorities);
                    SecurityContextHolder.getContext().setAuthentication(authentication);
                }
            } catch (ExpiredJwtException e) {
                log.error("Token过期");
            } catch (Exception e) {
                log.error("Token无效");
            }
        }
        filterChain.doFilter(request, response);
    }
}
UserAccessDecisionManager
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.stereotype.Component;

import java.util.Collection;
import java.util.Iterator;

/**
 * @Description: 访问决策管理器, 请求url权限验证
 */
@Component
@Slf4j
public class UserAccessDecisionManager implements AccessDecisionManager {
    @Override
    public void decide(Authentication authentication, Object o, Collection<ConfigAttribute> collection) throws AccessDeniedException, InsufficientAuthenticationException {

        Iterator<ConfigAttribute> iterator = collection.iterator();
        while (iterator.hasNext()) {
            ConfigAttribute ca = iterator.next();
            //当前请求需要的权限
            String needRole = ca.getAttribute();
            //当前用户所具有的权限
            Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();

            for (GrantedAuthority authority : authorities) {
                if (authority.getAuthority().equals(needRole)) {
                    return;
                }
            }
        }
        throw new AccessDeniedException("权限不足!");
    }

    @Override
    public boolean supports(ConfigAttribute configAttribute) {
        return true;
    }

    @Override
    public boolean supports(Class<?> aClass) {
        return true;
    }
}
UserAccessDeniedHandler
import com.alibaba.fastjson.JSON;
import com.wuyuan.cs.manage.common.utils.SifarResult;
import com.wuyuan.cs.manage.common.utils.WebError;
import lombok.extern.slf4j.Slf4j;
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.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Component
@Slf4j
public class UserAccessDeniedHandler implements AccessDeniedHandler {
    //权限拒绝处理逻辑
    @Override
    public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException, ServletException {
        httpServletResponse.setStatus(HttpServletResponse.SC_FORBIDDEN);
        SifarResult result = SifarResult.error(WebError.ERROR_MANAGER_ADMIN_INSUFFICIENT_PERMISSION,"权限不足");
        httpServletResponse.setContentType("text/json;charset=utf-8");
        log.error("权限不足!");
        httpServletResponse.getWriter().write(JSON.toJSONString(result));
    }
}
UserAuthenticationEntryPoint
import com.alibaba.fastjson.JSON;
import com.wuyuan.cs.manage.common.utils.SifarResult;
import com.wuyuan.cs.manage.common.utils.WebError;
import lombok.extern.slf4j.Slf4j;
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;

/**
 * @Description: 匿名用户访问无权限资源时的异常
 */
@Component
@Slf4j
public class UserAuthenticationEntryPoint implements AuthenticationEntryPoint {
    @Override
    public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
        SifarResult result = SifarResult.error(WebError.ERROR_MANAGER_ADMIN_HAS_NOT_LOGIN,"用户未登录");
        log.error("用户未登录!");
        httpServletResponse.setContentType("text/json;charset=utf-8");
        httpServletResponse.getWriter().write(JSON.toJSONString(result));
    }
}
UserAuthenticationFailureHandler
import com.alibaba.fastjson.JSON;
import com.wuyuan.cs.manage.common.utils.SifarResult;
import com.wuyuan.cs.manage.common.utils.WebError;
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.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @Description: 登录失败处理逻辑
 */
@Component
public class UserAuthenticationFailureHandler implements AuthenticationFailureHandler {


    @Override
    public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException, ServletException {
        //返回json数据
        SifarResult result = null;
        if (e instanceof AccountExpiredException) {
            //账号过期
            result = SifarResult.error(WebError.ERROR_MANAGER_ADMIN_SESSION_FAILURE,"session过期");
        } else if (e instanceof BadCredentialsException) {
            //密码错误
            result = SifarResult.error(WebError.ERROR_ADMIN_NAME_OR_PASSWORD,"账号或者用户名错误");
        } else if (e instanceof CredentialsExpiredException) {
            //密码过期
            result = SifarResult.error(WebError.ERROR_MANAGER_ADMIN_SESSION_FAILURE,"密码过期");
        } else if (e instanceof DisabledException) {
            //账号不可用
            result = SifarResult.error(WebError.ERROR_MANAGER_ADMIN_SESSION_FAILURE,"账号不可用");
        } else if (e instanceof LockedException) {
            //账号锁定
            result = SifarResult.error(WebError.ERROR_MANAGER_ADMIN_SESSION_FAILURE,"账号锁定");
        } else if (e instanceof InternalAuthenticationServiceException) {
            //用户不存在
            result = SifarResult.error(WebError.ERROR_USER_DOSE_NOT_EXIST,"用户不存在");
        }else{
            //其他错误
            result = SifarResult.error(WebError.ERROR_MANAGER_ADMIN_SESSION_FAILURE,"其他错误");
        }
       //处理编码方式,防止中文乱码的情况
        httpServletResponse.setContentType("text/json;charset=utf-8");
       //塞到HttpServletResponse中返回给前台
        httpServletResponse.getWriter().write(JSON.toJSONString(result));
    }
}
UserAuthenticationSuccessHandler
import com.alibaba.fastjson.JSON;
import com.wuyuan.cs.manage.common.dto.LoginDTO;
import com.wuyuan.cs.manage.common.pojo.Admin;
import com.wuyuan.cs.manage.common.utils.SifarResult;
import com.wuyuan.cs.manage.component.security.utils.JWTTokenUtil;
import com.wuyuan.cs.manage.service.AdminService;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;

/**
 * @Description: 登录成功处理逻辑
 */
@Component
@Slf4j
public class UserAuthenticationSuccessHandler implements AuthenticationSuccessHandler {
    @Autowired
    private AdminService adminService;

    @SneakyThrows
    @Override
    public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication){
        log.info("登录成功");
        // 将用户权限生成token
//        User userDetails = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        User userDetails = (User) authentication.getPrincipal();
        String token = JWTTokenUtil.createAccessToken(userDetails);
        token = JWTTokenUtil.TOKEN_PREFIX + token;

        // 将用户信息和权限菜单和token返回给前端
        Admin admin = adminService.getAdminByName(userDetails.getUsername());
        SifarResult<LoginDTO> login = adminService.login(admin);
        LoginDTO loginDTO = login.getContent();
        loginDTO.setToken(token);
        //处理编码方式,防止中文乱码的情况
        httpServletResponse.setContentType("application/json;charset=utf-8");
        //返回json数据
        SifarResult result = SifarResult.success(loginDTO);
        PrintWriter out = httpServletResponse.getWriter();
        out.write(JSON.toJSONString(result));
        out.flush();
        out.close();
    }
}
UserFilterInvocationSecurity
import com.wuyuan.cs.manage.common.pojo.PermissionNew;
import com.wuyuan.cs.manage.service.PermissionNewService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;

import java.util.Collection;
import java.util.List;

/**
 * @Description:
 */
@Component
public class UserFilterInvocationSecurity implements FilterInvocationSecurityMetadataSource {

    AntPathMatcher antPathMatcher = new AntPathMatcher();
    @Value("${server.servlet.context-path}")
    private String contextPath;

    @Autowired
    private PermissionNewService permissionNewService;
    @Override
    public Collection<ConfigAttribute> getAttributes(Object o) throws IllegalArgumentException {
        //获取请求地址
        String requestUrl = ((FilterInvocation) o).getHttpRequest().getRequestURI();
        if(requestUrl.contains("/actuator")){
            return null;
        }
        if (requestUrl.contains(contextPath)) {
            requestUrl = requestUrl.replace(contextPath, "");
        }
        List<PermissionNew> permissionList = permissionNewService.getPermissionNewListByUrl(requestUrl);
        if(permissionList == null || permissionList.size() == 0){
            //请求路径没有配置权限,表明该请求接口可以任意访问
            return null;
        }
        String[] attributes = new String[permissionList.size()];
        for(int i = 0;i<permissionList.size();i++){
            attributes[i] = permissionList.get(i).getEnname();
        }
        return SecurityConfig.createList(attributes);
    }

    @Override
    public Collection<ConfigAttribute> getAllConfigAttributes() {
        return null;
    }

    @Override
    public boolean supports(Class<?> aClass) {
        return true;
    }
}
UserLogoutSuccessHandler
import com.alibaba.fastjson.JSON;
import com.wuyuan.cs.manage.common.utils.SifarResult;
import lombok.extern.slf4j.Slf4j;
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;

/**
 * @Description: 登出成功处理逻辑
 */
@Component
@Slf4j
public class UserLogoutSuccessHandler implements LogoutSuccessHandler {
    @Override
    public void onLogoutSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
        SifarResult result = SifarResult.success("退出登录成功");
        log.info("退出登录成功");
        httpServletResponse.setContentType("text/json;charset=utf-8");
        httpServletResponse.getWriter().write(JSON.toJSONString(result));
    }
}
UserSessionInformationExpiredStrategy
import com.alibaba.fastjson.JSON;
import com.wuyuan.cs.manage.common.utils.SifarResult;
import com.wuyuan.cs.manage.common.utils.WebError;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.web.session.SessionInformationExpiredEvent;
import org.springframework.security.web.session.SessionInformationExpiredStrategy;
import org.springframework.stereotype.Component;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @Description: 会话信息过期策略
 */
@Component
@Slf4j
public class UserSessionInformationExpiredStrategy implements SessionInformationExpiredStrategy {
    @Override
    public void onExpiredSessionDetected(SessionInformationExpiredEvent sessionInformationExpiredEvent) throws IOException, ServletException {
        SifarResult result = SifarResult.error(WebError.ERROR_MANAGER_ADMIN_SESSION_FAILURE,"session过期");
        log.error("session过期");
        HttpServletResponse httpServletResponse = sessionInformationExpiredEvent.getResponse();
        httpServletResponse.setContentType("text/json;charset=utf-8");
        httpServletResponse.getWriter().write(JSON.toJSONString(result));
    }
}
JWTTokenUtil
import com.alibaba.fastjson.JSON;
import com.wuyuan.cs.manage.common.utils.TimeUtil;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.userdetails.UserDetails;

import java.text.ParseException;
import java.util.Date;

@Slf4j
public class JWTTokenUtil {
    /**
     *  JWT的过期时间
     */
//    // 过期时间是3600秒,既是1个小时
//    private static final long EXPIRATION = 3600L;
//
//    // 选择了记住我之后的过期时间为7天
//    private static final long EXPIRATION_REMEMBER = 604800L;
    /**
     *  过期时间,毫秒,一天
     */
    public static final long JWT_EXPIRE = 60 * 60 * 24*1000;
    /**
     * JWT的生成密钥
     */
    public static final String JWT_SECURITY = "JWTSecret";
    /**
     * 请求的 Header KEY
     */
    public static final String HEAD_KEY = "Authorization";
    /**
     * 生成的Token的前缀:
     */
    public static final String TOKEN_PREFIX = "YuChen-";//"Bearer "


    /**
     * 生成Token
     */
    public static String createAccessToken(UserDetails userDetails) throws ParseException {
//        long expiration = isRememberMe ? EXPIRATION_REMEMBER : EXPIRATION;
        String auth = JSON.toJSONString(userDetails.getAuthorities());
        System.out.println("========"  +userDetails.getPassword());
        // 登陆成功生成JWT
        String token = Jwts.builder()
                //设置jti(JWT ID):是JWT的唯一标识,根据业务需要,这个可以设置为一个不重复的值,主要用来作为一次性token,从而回避重放攻击。
                .setId(userDetails.getUsername())
                // 主题(代表这个JWT的主体,即它的所有人,这个是一个json格式的字符串,可以存放什么userid,roldid之类的,作为什么用户的唯一标志。)
                .setSubject(userDetails.getUsername())
                // 签发时间
                .setIssuedAt(TimeUtil.GetGreenDate())
                // 签发者
                .setIssuer("YuChen")
                // 自定义属性 放入用户拥有权限
                .claim("authorities", auth)
                // 失效时间
                .setExpiration(new Date(TimeUtil.GetGreenDate().getTime() + JWT_EXPIRE))
                // 设置签名使用的签名算法和签名使用的秘钥
                //生成签名的时候使用的秘钥secret,这个方法在本地封装,一般可以从本地配置文件中读取,切记这个秘钥不能外露哦。
                // 它就是你服务端的私钥,在任何场景都不应该流露出去。一旦客户端得知这个secret, 那就意味着客户端是可以自我签发jwt了。
                .signWith(SignatureAlgorithm.HS512, JWT_SECURITY)
                //就开始压缩为xxxxxxxxxxxxxx.xxxxxxxxxxxxxxx.xxxxxxxxxxxxx这样的jwt
                .compact();
        return token;
    }

    /**
     * @Description 解析JWT-Token
     */
    public static Claims parseJWT(String jwt) throws Exception {
        //得到DefaultJwtParser
        Claims claims = Jwts.parser()
                //设置签名的秘钥
                .setSigningKey(JWT_SECURITY)
                //设置需要解析的jwt
                .parseClaimsJws(jwt).getBody();
        return claims;
    }

    /**
     * 获取token的过期时间
     *
     * @param token token
     * @return 过期时间
     */
    public Date getExpirationDateFromToken(String token) throws Exception {
        return parseJWT(token)
                .getExpiration();
    }

    /**
     * 判断token是否过期
     *
     * @param token token
     * @return 已过期返回true,未过期返回false
     */
    private Boolean isTokenExpired(String token) throws Exception {
        Date expiration = getExpirationDateFromToken(token);
        return expiration.before(TimeUtil.GetGreenDate());
    }


}


PasswordEncoderMD5 

import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;

@Component
public class PasswordEncoderMD5 implements PasswordEncoder {

	@Override
	public String encode(CharSequence rawPassword) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public boolean matches(CharSequence rawPassword, String encodedPassword) {
		
		//rawPassword:客户端获取的密码	encodedPassword:数据库查询出的密码
		String rawPasswordMD5 = UserManager.md5Pswd(rawPassword.toString());
		return rawPasswordMD5.equals(encodedPassword);
	}

}
UserManager 
import cn.hutool.crypto.digest.DigestUtil;
import com.wuyuan.cs.manage.common.pojo.Admin;


public class UserManager {
   
   /**
    * 加工密码,和登录一致。
    * @param user
    * @return
    */
   public static Admin md5Pswd(Admin user){
      //密码为   email + '#' + pswd,然后MD5
      user.setPassword(md5Pswd(user.getUserName(),user.getPassword()));
      return user;
   }
   /**
    * 字符串返回值
    * @param /email
    * @param pswd
    * @return
    */
   public static String md5Pswd(String name ,String pswd){
      pswd = String.format("%s#%s", name,pswd);
      pswd = DigestUtil.md5Hex(pswd);
      return pswd;
   }
   public static String md5Pswd(String pswd){
      pswd = DigestUtil.md5Hex(pswd);
      return pswd;
   }

   public static void main(String[] args) {
      String pwd = md5Pswd("123456");
      System.out.println(pwd);
   }

}

UserDetailsServiceImpl
import cn.hutool.core.util.StrUtil;
import com.wuyuan.cs.manage.common.pojo.Admin;
import com.wuyuan.cs.manage.common.pojo.PermissionNew;
import com.wuyuan.cs.manage.common.pojo.Role;
import com.wuyuan.cs.manage.service.AdminService;
import com.wuyuan.cs.manage.service.PermissionNewService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
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;

@Service
public class UserDetailsServiceImpl implements UserDetailsService {

    @Autowired
    private AdminService adminService;
    @Autowired
    private PermissionNewService permissionNewService;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        Admin admin = adminService.getAdminByName(username);
        if(null == admin){
            throw new UsernameNotFoundException("用户不存在!");
        }

        // 查询用户拥有的角色集合
        List<Role> roleList = adminService.getRoleListById(admin.getId());
        // 查询用户拥有的权限集合
        List<PermissionNew> permissionList =  permissionNewService.getPermissionNewByUid(admin.getId());
        // 用户权限=【角色+权限】
        List<GrantedAuthority> authorities = new ArrayList<>();
        for (Role rolemap : roleList) {
            GrantedAuthority grantedAuthority = new SimpleGrantedAuthority("ROLE_" + rolemap.getRoleName());
            authorities.add(grantedAuthority);
        }
        for (PermissionNew permissionmap : permissionList) {
            if (!StrUtil.isEmpty(permissionmap.getEnname())) {
                GrantedAuthority grantedAuthority = new SimpleGrantedAuthority(permissionmap.getEnname());
                authorities.add(grantedAuthority);
            }
        }
        return new User(admin.getUserName(), admin.getPassword(), authorities);
    }

}
WebSecurityConfig
import com.wuyuan.cs.manage.component.security.handler.*;
import com.wuyuan.cs.manage.component.security.utils.PasswordEncoderMD5;
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.ObjectPostProcessor;
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.core.userdetails.UserDetailsService;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;

import javax.sql.DataSource;

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    //登录成功处理逻辑
    @Autowired
    UserAuthenticationSuccessHandler authenticationSuccessHandler;

    //登录失败处理逻辑
    @Autowired
    UserAuthenticationFailureHandler authenticationFailureHandler;

    //权限拒绝处理逻辑
    @Autowired
    UserAccessDeniedHandler accessDeniedHandler;

    //匿名用户访问无权限资源时的异常
    @Autowired
    UserAuthenticationEntryPoint authenticationEntryPoint;

    //会话失效(账号被挤下线)处理逻辑
//    @Autowired
//    UserSessionInformationExpiredStrategy sessionInformationExpiredStrategy;

    //登出成功处理逻辑
    @Autowired
    UserLogoutSuccessHandler logoutSuccessHandler;

    //访问决策管理器
    @Autowired
    UserAccessDecisionManager accessDecisionManager;

    //实现权限拦截
    @Autowired
    UserFilterInvocationSecurity securityMetadataSource;


    @Autowired
    UserDetailsService userDetailsService;

    @Autowired
    private DataSource dataSource; // 数据源

    @Override
    @Bean
    public UserDetailsService userDetailsService() {
        //获取用户账号密码及权限信息
        return new UserDetailsServiceImpl();
    }

    @Bean
    public PasswordEncoderMD5 passwordEncoder() {
        return new PasswordEncoderMD5();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService()).passwordEncoder(new PasswordEncoderMD5());
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //  开启跨域 ,取消跨站请求伪造防护
        http.cors().and().csrf().disable();
        http.authorizeRequests()
                .antMatchers("/actuator/**").permitAll()
                .antMatchers("/shell/**").permitAll()
                .withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
                    @Override
                    public <O extends FilterSecurityInterceptor> O postProcess(O o) {
                        o.setAccessDecisionManager(accessDecisionManager);//决策管理器
                        o.setSecurityMetadataSource(securityMetadataSource);//安全元数据源
                        return o;
                    }
                }).
                //登出
                and().logout().
                    permitAll().//允许所有用户
                    logoutSuccessHandler(logoutSuccessHandler).//登出成功处理逻辑
                    deleteCookies("JSESSIONID").//登出之后删除cookie
                //登入
                and().formLogin().
                    permitAll().//允许所有用户
                    successHandler(authenticationSuccessHandler).//登录成功处理逻辑
                    failureHandler(authenticationFailureHandler)//登录失败处理逻辑
                .and().rememberMe()
                .userDetailsService(userDetailsService) // 设置userDetailsService
//                .tokenRepository(persistentTokenRepository()) // 设置数据访问层
                .tokenValiditySeconds(60*60*24). // 记住我的时间(秒)
                //异常处理(权限拒绝、登录失效等)
                and().exceptionHandling().
                    accessDeniedHandler(accessDeniedHandler).//权限拒绝处理逻辑
                    authenticationEntryPoint(authenticationEntryPoint);//匿名用户访问无权限资源时的异常处理
                //会话管理
//                and().sessionManagement().
//                    maximumSessions(1).//同一账号同时登录最大用户数
//                    expiredSessionStrategy(sessionInformationExpiredStrategy);//会话失效(账号被挤下线)处理逻辑
//        http.addFilterBefore(securityInterceptor, JWTAuthenticationTokenFilter.class);
        // 基于Token不需要session
        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
        // 禁用缓存
        http.headers().cacheControl();
        // 添加JWT过滤器
        http.addFilter(new JWTAuthenticationTokenFilter(authenticationManager()));
    }

    /**
     * 持久化token
     *
     * Security中,默认是使用PersistentTokenRepository的子类InMemoryTokenRepositoryImpl,将token放在内存中
     * 如果使用JdbcTokenRepositoryImpl,会创建表persistent_logins,将token持久化到数据库
     */
   /* @Bean
    public PersistentTokenRepository persistentTokenRepository() {
        JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();
        tokenRepository.setDataSource(dataSource); // 设置数据源
//        tokenRepository.setCreateTableOnStartup(true); // 启动创建表,创建成功后注释掉
        return tokenRepository;
    }*/



}

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

落墨留白

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值