springBoot整合springsecurity、jwt-token实现权限验证

本文详细介绍了一个基于JWT的权限管理系统搭建过程,包括依赖包配置、表结构设计、核心配置类、工具类实现、服务层和控制器层代码。系统采用Spring Security进行权限控制,实现了用户登录、注销、权限校验等功能。
摘要由CSDN通过智能技术生成

一、jar包依赖:

        <!--jwt-->
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-jwt</artifactId>
            <version>1.0.9.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.0</version>
        </dependency>

        <!-- security -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>

        <!-- lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.6</version>
            <optional>true</optional>
        </dependency>

二、表结构初及数据始化:
 


CREATE TABLE IF NOT EXISTS `rbac_user` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
  `age` int(5) DEFAULT NULL COMMENT '年龄',
  `name` varchar(255) DEFAULT NULL COMMENT '姓名',
  `birth` datetime DEFAULT NULL COMMENT '生日',
  `account` varchar(20)  NOT NULL COMMENT '账号',
  `password` varchar(500) NOT NULL COMMENT '密码',
  `nick_name` varchar(20)  COMMENT '昵称',
  `email` varchar(100)  COMMENT '邮箱',
  `mobile_phone` varchar(20) COMMENT '手机',
  `avatar` varchar(500) COMMENT '头像地址',
  last_password_reset_date datetime DEFAULT NULL COMMENT '密码更新时间',
  status char(1) default 'A' COMMENT '状态:A 正常,D 删除',
  `version` bigint(20) DEFAULT '1' COMMENT '版本号',
  `created_time` datetime DEFAULT NULL COMMENT '创建时间',
  `created_by` bigint(20) DEFAULT NULL COMMENT '创建人',
  `updated_time` datetime DEFAULT NULL COMMENT '更新时间',
  `updated_by` bigint(20) DEFAULT NULL COMMENT '更新人',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户表';



-- 建表sql(role)
CREATE TABLE IF NOT EXISTS `rbac_role` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT ,
  `code` varchar(50)  NOT NULL COMMENT '代码',
  `name` varchar(100)  NOT NULL COMMENT '名称',
  `description` varchar(100)  COMMENT '描述',
  status char(1) default 'A' COMMENT '状态:A 正常,D 删除',
  `version` bigint(20) DEFAULT '1' COMMENT '版本号',
  `created_time` datetime DEFAULT NULL COMMENT '创建时间',
  `created_by` bigint(20) DEFAULT NULL COMMENT '创建人',
  `updated_time` datetime DEFAULT NULL COMMENT '更新时间',
  `updated_by` bigint(20) DEFAULT NULL COMMENT '更新人',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='角色表';


-- 建表sql(permission)
CREATE TABLE IF NOT EXISTS `rbac_permission` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT ,
  `code` varchar(50)  NOT NULL COMMENT '代码',
  `name` varchar(100)  NOT NULL COMMENT '名称',
  `description` varchar(100) ,
  status char(1) default 'A' COMMENT '状态:A 正常,D 删除',
  `version` bigint(20) DEFAULT '1' COMMENT '版本号',
  `created_time` datetime DEFAULT NULL COMMENT '创建时间',
  `created_by` bigint(20) DEFAULT NULL COMMENT '创建人',
  `updated_time` datetime DEFAULT NULL COMMENT '更新时间',
  `updated_by` bigint(20) DEFAULT NULL COMMENT '更新人',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='权限表';


-- 建表sql(user_role)
CREATE TABLE IF NOT EXISTS `rbac_user_role` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT ,
  `user_id` bigint(20) NOT NULL,
  `role_id` bigint(20) NOT NULL,
  status char(1) default 'A' COMMENT '状态:A 正常,D 删除',
  `version` bigint(20) DEFAULT '1' COMMENT '版本号',
  `created_time` datetime DEFAULT NULL COMMENT '创建时间',
  `created_by` bigint(20) DEFAULT NULL COMMENT '创建人',
  `updated_time` datetime DEFAULT NULL COMMENT '更新时间',
  `updated_by` bigint(20) DEFAULT NULL COMMENT '更新人',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户角色表';


-- 建表sql(role_permission)
CREATE TABLE IF NOT EXISTS `rbac_role_permission` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT ,
  `role_id` bigint(20) NOT NULL,
	`permission_id` bigint(20) NOT NULL,
	status char(1) default 'A' COMMENT '状态:A 正常,D 删除',
	`version` bigint(20) DEFAULT '1' COMMENT '版本号',
  `created_time` datetime DEFAULT NULL COMMENT '创建时间',
  `created_by` bigint(20) DEFAULT NULL COMMENT '创建人',
  `updated_time` datetime DEFAULT NULL COMMENT '更新时间',
  `updated_by` bigint(20) DEFAULT NULL COMMENT '更新人',
	PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='角色权限表';

-- 初始化用户
INSERT INTO `gourd`.`rbac_user`(`id`, `age`, `name`, `birth`, `account`, `password`, `nick_name`, `email`, `last_password_reset_date`, `mobile_phone`, `avatar`, `status`, `version`, `created_time`, `created_by`, `updated_time`, `updated_by`) VALUES (1, 0, 'admin', '2019-04-25 18:43:07', 'admin', '$2a$10$o1avyPI98TdBco3m7JgCTuhPQaasSy/J2EqDH9XX46rxggjviWMzO', 'admin', NULL, NULL, '13584278267', NULL, 'A', 1, NULL, NULL, NULL, NULL);
INSERT INTO `gourd`.`rbac_user`(`id`, `age`, `name`, `birth`, `account`, `password`, `nick_name`, `email`, `last_password_reset_date`, `mobile_phone`, `avatar`, `status`, `version`, `created_time`, `created_by`, `updated_time`, `updated_by`) VALUES (2, 0, 'gourd', '2019-04-08 09:06:56', 'gourd', '$2a$10$o1avyPI98TdBco3m7JgCTuhPQaasSy/J2EqDH9XX46rxggjviWMzO', 'gourd', NULL, '2019-04-08 09:12:56', '13584278267', NULL, 'A', 1, NULL, NULL, NULL, NULL);

-- 初始化角色
INSERT INTO `gourd`.`rbac_role`(`id`, `code`, `name`, `description`, `version`, `created_time`, `created_by`, `updated_time`, `updated_by`, `status`) VALUES (1, 'ADMIN', '管理员', NULL, 1, NULL, NULL, NULL, NULL, 'A');
INSERT INTO `gourd`.`rbac_role`(`id`, `code`, `name`, `description`, `version`, `created_time`, `created_by`, `updated_time`, `updated_by`, `status`) VALUES (2, 'EMPLOYEE', '员工', NULL, 1, NULL, NULL, NULL, NULL, 'A');
INSERT INTO `gourd`.`rbac_role`(`id`, `code`, `name`, `description`, `version`, `created_time`, `created_by`, `updated_time`, `updated_by`, `status`) VALUES (3, 'USER', '用户', '注册的普通用户', 1, NULL, NULL, NULL, NULL, 'A');

-- 初始化用户权限
INSERT INTO `gourd`.`rbac_user_role`(`id`, `user_id`, `role_id`, `version`, `created_time`, `created_by`, `updated_time`, `updated_by`, `status`) VALUES (1, 1, 1, 0, NULL, NULL, NULL, NULL, 'A');
INSERT INTO `gourd`.`rbac_user_role`(`id`, `user_id`, `role_id`, `version`, `created_time`, `created_by`, `updated_time`, `updated_by`, `status`) VALUES (2, 2, 3, 0, NULL, NULL, NULL, NULL, 'A');

-- 初始化权限
INSERT INTO `gourd`.`rbac_permission`(`id`, `code`, `name`, `description`, `status`, `version`, `created_time`, `created_by`, `updated_time`, `updated_by`) VALUES (1, 'READ', '阅读权限', NULL, 'A', 1, NULL, NULL, NULL, NULL);
INSERT INTO `gourd`.`rbac_permission`(`id`, `code`, `name`, `description`, `status`, `version`, `created_time`, `created_by`, `updated_time`, `updated_by`) VALUES (2, 'WRITE', '写权限', NULL, 'A', 1, NULL, NULL, NULL, NULL);

-- 初始化角色权限
INSERT INTO `gourd`.`rbac_role_permission`(`id`, `role_id`, `permission_id`, `status`, `version`, `created_time`, `created_by`, `updated_time`, `updated_by`) VALUES (1, 1, 1, 'A', 1, NULL, NULL, NULL, NULL);
INSERT INTO `gourd`.`rbac_role_permission`(`id`, `role_id`, `permission_id`, `status`, `version`, `created_time`, `created_by`, `updated_time`, `updated_by`) VALUES (2, 1, 2, 'A', 1, NULL, NULL, NULL, NULL);
INSERT INTO `gourd`.`rbac_role_permission`(`id`, `role_id`, `permission_id`, `status`, `version`, `created_time`, `created_by`, `updated_time`, `updated_by`) VALUES (3, 2, 1, 'A', 1, NULL, NULL, NULL, NULL);
INSERT INTO `gourd`.`rbac_role_permission`(`id`, `role_id`, `permission_id`, `status`, `version`, `created_time`, `created_by`, `updated_time`, `updated_by`) VALUES (4, 3, 1, 'A', 1, NULL, NULL, NULL, NULL);



三、配置项及配置类

spring:
  #是否开启spring security基本的鉴权
  security.basic.enabled: true

# JWT
jwt:
  header: jwt-token
  secret: mySecret
  #token有效期一天(秒)
  expiration: 86400

核心配置类


import com.gourd.common.exception.GoAuthenticationEntryPoint;
import com.gourd.common.filter.JwtAuthenticationTokenFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.BeanIds;
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.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

/**
 * @Author: gourd.
 * @Description: SpringSecurity 核心配置类
 */
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private JwtAuthenticationTokenFilter jwtAuthenticationTokenFilter;
    @Autowired
    private UserDetailsService userDetailsService;
    @Value("${jwt.expiration}")
    private int validate;

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //添加自定义的userDetailsService认证
        auth.userDetailsService(this.userDetailsService).passwordEncoder(passwordEncoder());
    }

    /**
     * 装载BCrypt密码编码器
     * @return
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean(name = BeanIds.AUTHENTICATION_MANAGER)
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http.csrf().disable()
                .cors().and()
                // 使用 JWT,关闭token
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .httpBasic()
                // 未经过认证的用户访问受保护的资源
                .authenticationEntryPoint(new GoAuthenticationEntryPoint())
                .and()
                .authorizeRequests()
                // 任何用户都可以访问URL以"/resources/", equals "/signup", 或者 "/about"开头的URL。
                //以 "/admin/" 开头的URL只能由拥有 "ROLE_ADMIN"角色的用户访问。请注意我们使用 hasRole 方法
                .antMatchers("/admin").hasRole("ADMIN")
                .antMatchers("/user").access("hasRole('USER') or hasRole('ADMIN') ")
                .antMatchers("/employee").access("hasRole('EMPLOYEE') or hasRole('ADMIN') ")
                .anyRequest().authenticated()
                .and()
                .formLogin()
                .loginPage("/login.html")
                .loginProcessingUrl("/index.html")
                .defaultSuccessUrl("/index.html", true).failureUrl("/error.html")
                .permitAll()
                .and()
                .logout()
                .logoutUrl("/login/logout")
                .permitAll()
                // 防止iframe 造成跨域
                .and().headers().frameOptions().disable();
        // 记住我
        http.rememberMe().rememberMeParameter("remember-me")
                .userDetailsService(userDetailsService).tokenValiditySeconds(validate);

        http.exceptionHandling()
                .and().addFilterBefore(jwtAuthenticationTokenFilter, UsernamePasswordAuthenticationFilter.class);
    }

    @Override
    public void configure(WebSecurity web) {
        web.ignoring()
                .antMatchers(
                        "/",
                        "/dic/**",
                        "/nacos/**",
                        "/mail/**",
                        "/excel/**",
                        "/file/**",
                        "/spider/**",
                        "/websocket/**",
                        "/user-es/**",
                        "/socket/**",
                        "/feign/**",
                        "/eureka/**",
                        "/quartz/**",
                        "/gourd/**",
                        "/kafka/**",
                        "/openOffice/**",
                        "/login/**",
                        "/auth/**",
                        "/api/**",
                        "/resources/**",
                        "/templates/**",
                        "/druid/*",
                        "/login.html",
                        "/websocket.html",
                        "/error.html",
                        "/file.html",
                        "*.css",
                        "*.js",
                        "*.gif","*.jpg", "*.png", "*.ico",
                        "/swagger-ui.html",
                        "/swagger-resources/**",
                        "/v2/**",
                        "/webjars/**");
    }


}

拦截器:

import com.gourd.common.exception.ServiceException;
import com.gourd.common.rbac.vo.JwtUser;
import com.gourd.common.utils.JwtUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

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.Collection;

/**
 * @author gourd
 */
@Component
@Slf4j
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
 
    @Value("${jwt.header}")
    private String tokenHeader;
 
    @Autowired
    private JwtUtil jwtTokenUtil;
    @Override
    protected void doFilterInternal(
            HttpServletRequest request,
            HttpServletResponse response,
            FilterChain chain) throws ServletException, IOException {
        String authToken = request.getHeader(this.tokenHeader);
        // 获取用户信息
        UserDetails userDetails = jwtTokenUtil.getUserFromToken(authToken);
        if(userDetails != null){
            String username = ((JwtUser) userDetails).getAccount();
            log.info("checking authentication " + username);
            if (username != null && jwtTokenUtil.containToken(username, authToken) && SecurityContextHolder.getContext().getAuthentication() == null) {
                if (jwtTokenUtil.validateToken(authToken)) {
                    Collection<? extends GrantedAuthority> authorities = userDetails.getAuthorities();
                    UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
                            userDetails, null, authorities);
                    authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(
                            request));
                    log.info("authenticated user " + username + ", setting security context");
                    SecurityContextHolder.getContext().setAuthentication(authentication);
                }else {
                    throw new ServiceException("token失效,请重新登录");
                }
            }
        }
        chain.doFilter(request, response);
    }
}

四、权限相关基本类

1.身份验证异常处理类

import com.alibaba.fastjson.JSON;
import com.gourd.common.data.BaseResponse;
import org.springframework.http.HttpStatus;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;

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

/**
 * @Author: gourd.
 * @Description: 它负责启动未经过身份验证的用户的身份验证过程(当他们试图访问受保护的资源
 * @Date:Created in 2018/8/25 23:11.
 */
public class GoAuthenticationEntryPoint implements AuthenticationEntryPoint {
    @Override
    public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException {
        httpServletResponse.setHeader("Content-Type", "application/json;charset=utf-8");
        BaseResponse baseResponse =new BaseResponse(HttpStatus.UNAUTHORIZED.value(),"身份认证失败,请重新登录");
        httpServletResponse.getWriter().write(JSON.toJSONString(baseResponse));
        httpServletResponse.getWriter().flush();
    }
}

2.userDetails类 核心用户类

import com.gourd.common.rbac.entity.RbacUser;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import java.util.*;

/**
 * @author gourd
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class JwtUser implements UserDetails {

    private Long id;
    private String name;
    private String account;
    private String password;
    /**
     * 密码更新时间
     */
    private Date lastPasswordResetDate;
    /**
     * 权限
     */
    private Collection<? extends GrantedAuthority> authorities;


    /**
     * 写一个能直接使用user创建jwtUser的构造器
     * @param user
     */
    public JwtUser(RbacUser user) {
        this.id = user.getId();
        this.name = user.getName();
        this.account = user.getAccount();
        this.password = user.getPassword();
        this.lastPasswordResetDate = user.getLastPasswordResetDate();
        Set<SimpleGrantedAuthority> simpleGrantedAuthorities = new HashSet<>();
        if(CollectionUtils.isNotEmpty(user.getAuthorities())){
            for(String code : user.getAuthorities()){
                SimpleGrantedAuthority simpleGrantedAuthority = new SimpleGrantedAuthority(code);
                simpleGrantedAuthorities.add(simpleGrantedAuthority);
            }
        }
        this.authorities = simpleGrantedAuthorities;
    }
    public JwtUser(Long id,String name,String account,List<String> authorities) {
        this.id =id;
        this.name = name;
        this.account = account;
        Set<SimpleGrantedAuthority> simpleGrantedAuthorities = new HashSet<>();
        if(CollectionUtils.isNotEmpty(authorities)){
            for(String code : authorities){
                SimpleGrantedAuthority simpleGrantedAuthority = new SimpleGrantedAuthority(code);
                simpleGrantedAuthorities.add(simpleGrantedAuthority);
            }
        }
        this.authorities = simpleGrantedAuthorities;

    }

    /**
     * 获取权限信息,目前只会拿来存角色
     * @return
     */
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return authorities;
    }

    @Override
    public String getPassword() {
        return password;
    }

    @Override
    public String getUsername() {
        return account;
    }


    @Override
    public boolean isAccountNonExpired() {
        // 账号是否未过期,默认是false,记得要改一下
        return true;
    }


    @Override
    public boolean isAccountNonLocked() {
        // 账号是否未锁定,默认是false,记得也要改一下
        return true;
    }


    @Override
    public boolean isCredentialsNonExpired() {
        // 账号凭证是否未过期,默认是false,记得还要改一下
        return true;
    }


    @Override
    public boolean isEnabled() {
        // 这个有点抽象不会翻译,默认也是false,记得改一下
        return true;
    }

}

3、登录成功返回对象

package com.gourd.web.rbac.auth;

import lombok.AllArgsConstructor;
import lombok.Data;

/**
 * @author gourd
 * createAt: 2018/9/17
 */
@Data
@AllArgsConstructor
public class ResponseUserToken {
    private String token;
    private JwtUser userDetail;
}

五、具体工具类

import com.gourd.common.rbac.vo.JwtUser;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.CompressionCodecs;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author: gourd
 * createAt: 2018/9/14
 */
@Component
@RefreshScope
@Slf4j
public class JwtUtil {
    private static final String CLAIM_KEY_ACCOUNT = "account";
    private static final String CLAIM_KEY_USERNAME = "name";
    private static final String CLAIM_KEY_CREATED = "created";
    private static final String CLAIM_KEY_USER_ID = "id";
    private static final String CLAIM_KEY_AUTHORITIES = "scope";

    private Map<String, String> tokenMap = new ConcurrentHashMap<>(32);

    @Value("${jwt.secret}")
    private String secret;

    @Value("${jwt.expiration}")
    private Long access_token_expiration;

    @Value("${jwt.expiration}")
    private Long refresh_token_expiration;

    private final SignatureAlgorithm SIGNATURE_ALGORITHM = SignatureAlgorithm.HS256;

    /**
     * 获取当用户
     * @return
     */
    public static JwtUser getUserInfo(){
        JwtUser user = (JwtUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        return user;

    }

    /**
     * 根据token获取用户信息
     * @param token
     * @return
     */
    public JwtUser getUserFromToken(String token) {
        JwtUser userDetail;
        try {
            final Claims claims = getClaimsFromToken(token);
            Long userId = Long.parseLong(String.valueOf(claims.get(CLAIM_KEY_USER_ID)));
            String userName = String.valueOf(claims.get(CLAIM_KEY_USERNAME));
            String account = claims.getSubject();
            Object o = claims.get(CLAIM_KEY_AUTHORITIES);
            List<String> authorities = (List<String>) claims.get(CLAIM_KEY_AUTHORITIES);
            userDetail = new JwtUser(userId,userName,account,authorities );
        } catch (Exception e) {
            userDetail = null;
        }

        return userDetail;
    }

    public long getUserIdFromToken(String token) {
        long userId = 0;
        try {
            final Claims claims = getClaimsFromToken(token);
            userId = Long.parseLong(String.valueOf(claims.get(CLAIM_KEY_USER_ID)));
        } catch (Exception e) {
            log.error("获取用户id异常:{}",e);
        }
        return userId;
    }

    /**
     * 获取用户账号
     * @param token
     * @return
     */
    public String getUsernameFromToken(String token) {
        String account =null;
        try {
            final Claims claims = getClaimsFromToken(token);
            account = claims.getSubject();
        } catch (Exception e) {
            log.error("获取用户账号异常:{}",e);
        }
        return account;
    }

    /**
     * 获取token创建时间
     *
     * @param token
     * @return
     */
    public Date getCreatedDateFromToken(String token) {
        Date created;
        try {
            final Claims claims = getClaimsFromToken(token);
            created = claims.getIssuedAt();
        } catch (Exception e) {
            created = null;
        }
        return created;
    }

    /**
     * 生成token
     * @param userDetail
     *
     * @return
     */
    public String generateAccessToken(JwtUser userDetail) {
        Map<String, Object> claims = generateClaims(userDetail);
        claims.put(CLAIM_KEY_AUTHORITIES, authoritiesToArray(userDetail.getAuthorities()));
        String accessToken = generateAccessToken(userDetail.getUsername(), claims);
        //存储token
        putToken(userDetail.getUsername(), accessToken);
        return accessToken;
    }

    /**
     * 获取过期时间
     *
     * @param token
     * @return
     */
    public Date getExpirationDateFromToken(String token) {
        Date expiration;
        try {
            final Claims claims = getClaimsFromToken(token);
            expiration = claims.getExpiration();
        } catch (Exception e) {
            expiration = null;
        }
        return expiration;
    }

    public Boolean canTokenBeRefreshed(String token, Date lastPasswordReset) {
        final Date created = getCreatedDateFromToken(token);
        return !isCreatedBeforeLastPasswordReset(created, lastPasswordReset)
                && (!isTokenExpired(token));
    }

    /**
     * 刷新token
     *
     * @param token
     * @return
     */
    public String refreshToken(String token) {
        String refreshedToken;
        try {
            final Claims claims = getClaimsFromToken(token);
            refreshedToken = generateAccessToken(claims.getSubject(), claims);
            //存储token
            putToken(claims.getSubject(), refreshedToken);
        } catch (Exception e) {
            refreshedToken = null;
        }
        return refreshedToken;
    }

    /**
     * 校验token是否失效
     *
     * @param token
     * @return
     */
    public Boolean validateToken(String token) {
        return (!isTokenExpired(token));
    }


    public void putToken(String userName, String token) {
        tokenMap.put(userName, token);
    }

    public void deleteToken(String userName) {
        tokenMap.remove(userName);
    }

    public boolean containToken(String userName, String token) {
        if (userName != null && tokenMap.containsKey(userName) && tokenMap.get(userName).equals(token)) {
            return true;
        }
        return false;
    }

    /**
     * 获取Claims
     *
     * @param token
     * @return
     */
    public Claims getClaimsFromToken(String token) {
        Claims claims;
        try {
            claims = Jwts.parser()
                    .setSigningKey(secret)
                    .parseClaimsJws(token)
                    .getBody();
        } catch (Exception e) {
            claims = null;
        }
        return claims;
    }

    private Date generateExpirationDate(long expiration) {
        return new Date(System.currentTimeMillis() + expiration * 1000);
    }

    private Boolean isTokenExpired(String token) {
        final Date expiration = getExpirationDateFromToken(token);
        return expiration.before(new Date());
    }

    private Boolean isCreatedBeforeLastPasswordReset(Date created, Date lastPasswordReset) {
        return (lastPasswordReset != null && created.before(lastPasswordReset));
    }

    private Map<String, Object> generateClaims(JwtUser userDetail) {
        Map<String, Object> claims = new HashMap<>(16);
        claims.put(CLAIM_KEY_ACCOUNT, userDetail.getUsername());
        claims.put(CLAIM_KEY_USERNAME, userDetail.getName());
        claims.put(CLAIM_KEY_CREATED, new Date());
        claims.put(CLAIM_KEY_USER_ID, userDetail.getId());
        return claims;
    }

    private String generateAccessToken(String subject, Map<String, Object> claims) {
        return generateToken(subject, claims, access_token_expiration);
    }

    private Set authoritiesToArray(Collection<? extends GrantedAuthority> authorities) {
        Set<String> list = new HashSet<>();
        for (GrantedAuthority ga : authorities) {
            list.add(ga.getAuthority());
        }
        return list;
    }



    private String generateToken(String subject, Map<String, Object> claims, long expiration) {
        return Jwts.builder()
                .setClaims(claims)
                .setSubject(subject)
                .setId(UUID.randomUUID().toString())
                .setIssuedAt(new Date())
                .setExpiration(generateExpirationDate(expiration))
                .compressWith(CompressionCodecs.DEFLATE)
                .signWith(SIGNATURE_ALGORITHM, secret)
                .compact();
    }

}

六、service层


import com.gourd.common.annotation.TargetDataSource;
import com.gourd.common.data.DataSourceNames;
import com.gourd.common.exception.BadRequestException;
import com.gourd.common.rbac.vo.JwtUser;
import com.gourd.common.data.ResponseUserToken;
import com.gourd.common.rbac.dto.RbacUserDTO;
import com.gourd.common.rbac.entity.RbacUser;
import com.gourd.common.rbac.service.AuthService;
import com.gourd.common.rbac.service.RbacUserService;
import com.gourd.common.utils.JwtUtil;
import com.gourd.common.rbac.vo.UserVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/**
 * @author: gourd
 * createAt: 2018/9/17
 */
@Service
@Slf4j
@Transactional(rollbackFor = Exception.class)
@RefreshScope
public class AuthServiceImpl implements AuthService {
    private final AuthenticationManager authenticationManager;
    private final UserDetailsService userDetailsService;
    private final JwtUtil jwtTokenUtil;
    private final RbacUserService rbacUserService;

    @Autowired
    public AuthServiceImpl(AuthenticationManager authenticationManager, @Qualifier("userDetailsService") UserDetailsService userDetailsService,
                           JwtUtil jwtTokenUtil, RbacUserService rbacUserService ) {
        this.authenticationManager = authenticationManager;
        this.userDetailsService = userDetailsService;
        this.jwtTokenUtil = jwtTokenUtil;
        this.rbacUserService = rbacUserService;
    }

    @Override
    public UserVO register(RbacUserDTO rbacUserDTO) {
        UserVO userDetail = new UserVO();
        RbacUser rbacUser = rbacUserService.save(rbacUserDTO);
        BeanUtils.copyProperties(rbacUser,userDetail);
        return userDetail;
    }

    @Override
    @TargetDataSource(DataSourceNames.SLAVE_DATASOURCE)
    public ResponseUserToken login(String username, String password) {
        //用户验证
        final Authentication authentication = authenticate(username, password);
        //存储认证信息
        SecurityContextHolder.getContext().setAuthentication(authentication);
        //生成token
        final JwtUser userDetail = (JwtUser) authentication.getPrincipal();
        final String token = jwtTokenUtil.generateAccessToken(userDetail);
        return new ResponseUserToken(token, userDetail);

    }

    @Override
    public void logout(String token) {
        String userName = jwtTokenUtil.getUsernameFromToken(token);
        jwtTokenUtil.deleteToken(userName);
    }

    @Override
    public ResponseUserToken refresh(String token) {
        String username = jwtTokenUtil.getUsernameFromToken(token);
        JwtUser userDetail = (JwtUser) userDetailsService.loadUserByUsername(username);
        if (jwtTokenUtil.canTokenBeRefreshed(token, userDetail.getLastPasswordResetDate())){
            token =  jwtTokenUtil.refreshToken(token);
            return new ResponseUserToken(token, userDetail);
        }
        return null;
    }

    @Override
    public JwtUser getUserByToken(String token) {
        return jwtTokenUtil.getUserFromToken(token);
    }


    private Authentication authenticate(String username, String password) {
        try {
            //该方法会去调用userDetailsService.loadUserByUsername()去验证用户名和密码,如果正确,则存储该用户名密码到“security 的 context中”
            return authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));
        } catch (DisabledException | BadCredentialsException e) {
            throw new BadRequestException("用户名或密码无效");
        }
    }
}

七、通用异常处理类:


import com.gourd.common.data.ApiError;
import com.gourd.common.data.BaseResponse;
import com.gourd.common.exception.BadRequestException;
import com.gourd.common.exception.EntityExistException;
import com.gourd.common.exception.EntityNotFoundException;
import com.gourd.common.utils.ThrowableUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.ui.Model;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;

import static org.springframework.http.HttpStatus.*;

/**
 * 异常处理器
 * @author gourd
 */
@RestControllerAdvice
@Slf4j
public class BusinessExceptionHandler {

	/**
	 * 应用到所有@RequestMapping注解方法,在其执行之前初始化数据绑定器
	 * @param binder
	 */
	@InitBinder
	public void initBinder(WebDataBinder binder) {
		System.out.println("请求有参数才进来 "+binder.getObjectName());
	}
 
	/**
	 * 把值绑定到Model中,使全局@RequestMapping可以获取到该值
	 * @param model
	 */
	@ModelAttribute
	public void addAttributes(Model model) {
		model.addAttribute("author", "gourd");
	}


	/**
	 * 处理 接口无权访问异常AccessDeniedException
	 * @param e
	 * @return
	 */
	@ExceptionHandler(AccessDeniedException.class)
	public ResponseEntity handleAccessDeniedException(AccessDeniedException e){
		// 打印堆栈信息
		log.error(ThrowableUtil.getStackTrace(e));
		ApiError apiError = new ApiError(FORBIDDEN.value(),e.getMessage());
		return buildResponseEntity(apiError);
	}

	/**
	 * 处理自定义异常
	 * @param e
	 * @return
	 */
	@ExceptionHandler(value = BadRequestException.class)
	public ResponseEntity<ApiError> badRequestException(BadRequestException e) {
		// 打印堆栈信息
		log.error(ThrowableUtil.getStackTrace(e));
		ApiError apiError = new ApiError(e.getStatus(),e.getMessage());
		return buildResponseEntity(apiError);
	}

	/**
	 * 处理 EntityExist
	 * @param e
	 * @return
	 */
	@ExceptionHandler(value = EntityExistException.class)
	public ResponseEntity<ApiError> entityExistException(EntityExistException e) {
		// 打印堆栈信息
		log.error(ThrowableUtil.getStackTrace(e));
		ApiError apiError = new ApiError(BAD_REQUEST.value(),e.getMessage());
		return buildResponseEntity(apiError);
	}

	/**
	 * 处理 EntityNotFound
	 * @param e
	 * @return
	 */
	@ExceptionHandler(value = EntityNotFoundException.class)
	public ResponseEntity<ApiError> entityNotFoundException(EntityNotFoundException e) {
		// 打印堆栈信息
		log.error(ThrowableUtil.getStackTrace(e));
		ApiError apiError = new ApiError(NOT_FOUND.value(),e.getMessage());
		return buildResponseEntity(apiError);
	}
	/**
	 * 处理所有不可知的异常
	 * @param e
	 * @return
	 */
	@ExceptionHandler(Exception.class)
	public Object handleException(Exception e, HttpServletRequest req){
		BaseResponse response = new BaseResponse();
		response.setCode(INTERNAL_SERVER_ERROR.value());
		if(e.getMessage().length()>500){
			response.setMsg("未知异常,请联系管理员");
		}else {
			response.setMsg(e.getMessage());
		}
		//使用HttpServletRequest中的header检测请求是否为ajax, 如果是ajax则返回json, 如果为非ajax则返回view(即ModelAndView)
		String contentTypeHeader = req.getHeader("Content-Type");
		String acceptHeader = req.getHeader("Accept");
		String xRequestedWith = req.getHeader("X-Requested-With");
		// 控制台打印log
		e.printStackTrace();
		if ((contentTypeHeader != null && contentTypeHeader.contains("application/json"))
				|| (acceptHeader != null && acceptHeader.contains("application/json"))
				|| "XMLHttpRequest".equalsIgnoreCase(xRequestedWith)) {
			return response;
		} else {
			ModelAndView modelAndView = new ModelAndView();
			modelAndView.addObject("msg", e.getMessage());
			modelAndView.addObject("url", req.getRequestURL());
			modelAndView.addObject("stackTrace", e.getStackTrace());
			modelAndView.setViewName("error");
			return modelAndView;
		}
	}

	/**
	 * 统一返回
	 * @param apiError
	 * @return
	 */
	private ResponseEntity<ApiError> buildResponseEntity(ApiError apiError) {
		return new ResponseEntity(apiError, HttpStatus.valueOf(apiError.getStatus()));
	}
}

八、controller层

1.注册登录登出等基本权限接口

package com.gourd.common.controller;

import com.gourd.common.data.BaseResponse;
import com.gourd.common.exception.BadRequestException;
import com.gourd.common.rbac.vo.JwtUser;
import com.gourd.common.data.ResponseUserToken;
import com.gourd.common.rbac.dto.RbacUserDTO;
import com.gourd.common.rbac.dto.RbacUserLoginDTO;
import com.gourd.common.rbac.service.AuthService;
import com.gourd.common.rbac.vo.UserVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;

/**
 * @author gourd
 * createAt: 2018/9/17
 */
@RestController
@Api(tags = "auth",description = "权限控制器")
@RequestMapping("/auth")
@RefreshScope
public class AuthController {
    @Value("${jwt.header}")
    private String tokenHeader;

    @Autowired
    private AuthService authService;


    @PostMapping(value = "/login")
    @ApiOperation(value = "登陆", notes = "登陆成功返回token,测试管理员账号:admin,123456;用户账号:les123,admin")
    public BaseResponse<ResponseUserToken> login(
            @Valid @RequestBody RbacUserLoginDTO user){
        ResponseUserToken response = authService.login(user.getAccount(), user.getPassword());
        return BaseResponse.ok(response);
    }

    @GetMapping(value = "/logout" )
    @ApiOperation(value = "登出", notes = "退出登陆")
    @ApiImplicitParams({@ApiImplicitParam(name = "jwt-token", value = "jwt-token", required = true, dataType = "string", paramType = "header")})
    public BaseResponse logout(HttpServletRequest request){
        String token = request.getHeader(tokenHeader);
        authService.logout(token);
        return BaseResponse.ok("登出成功");
    }

    @GetMapping(value = "/user")
    @ApiOperation(value = "根据token获取用户信息", notes = "根据token获取用户信息")
    @ApiImplicitParams({@ApiImplicitParam(name = "jwt-token", value = "jwt-token", required = true, dataType = "string", paramType = "header")})
    public BaseResponse<JwtUser> getUser(HttpServletRequest request){
        String token = request.getHeader(tokenHeader);
        JwtUser userDetail = authService.getUserByToken(token);
        return BaseResponse.ok(userDetail);
    }

    @PostMapping(value = "/sign")
    @ApiOperation(value = "用户注册")
    public BaseResponse<UserVO> sign(@RequestBody RbacUserDTO user) {
        return BaseResponse.ok(authService.register(user));
    }

    @GetMapping(value = "refresh")
    @ApiOperation(value = "刷新token")
    @ApiImplicitParams({@ApiImplicitParam(name = "jwt-token", value = "jwt-token", required = true, dataType = "string", paramType = "header")})
    public BaseResponse<ResponseUserToken> refreshAndGetAuthenticationToken(
            HttpServletRequest request){
        String token = request.getHeader(tokenHeader);
        ResponseUserToken response = authService.refresh(token);
        if(response == null) {
            throw new BadRequestException("token无效");
        } else {
            return BaseResponse.ok(response);
        }
    }
}

2、权限校验contoller:

通过注解  @PreAuthorize("hasAuthority('READ')") 、@PreAuthorize("hasRole('ROLE_ADMIN')") 标识接口权限

import com.baomidou.mybatisplus.plugins.Page;
import com.gourd.common.annotation.Log;
import com.gourd.common.rbac.dto.RbacUserDTO;
import com.gourd.common.rbac.dto.RbacUserSearchDTO;
import com.gourd.common.rbac.entity.RbacUser;
import com.gourd.common.rbac.service.RbacUserService;
import com.gourd.common.rbac.vo.JwtUser;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import java.util.List;

/**
 * @author gourd
 */
@RestController
@RequestMapping("/user")
@Api(tags = "user", description = "用户控制器")
public class UserController{

    @Autowired
    private RbacUserService userService;


    @GetMapping("/current")
    @ApiOperation(value = "获取当前用户")
    @PreAuthorize("hasAuthority('READ')")
    @ApiImplicitParams({@ApiImplicitParam(name = "jwt-token", value = "jwt-token", required = true, dataType = "string", paramType = "header")})
    public JwtUser getCurrent() {
        return userService.getCurrent();
    }

    @GetMapping("/all")
    @ApiOperation(value = "获取所有的用户")
    @PreAuthorize("hasRole('ROLE_ADMIN')")
    @ApiImplicitParams({@ApiImplicitParam(name = "jwt-token", value = "jwt-token", required = true, dataType = "string", paramType = "header")})
    public List<RbacUser> findAll() {
        return userService.findAll();
    }

    @GetMapping("/page")
    @ApiOperation(value = "根据条件获取用户分页")
    @PreAuthorize("hasRole('ROLE_ADMIN')")
    @ApiImplicitParams({@ApiImplicitParam(name = "jwt-token", value = "jwt-token", required = true, dataType = "string", paramType = "header")})
    public Page<RbacUser> find(RbacUserSearchDTO rbacUserDTO) {
        PageRequest pageRequest = new PageRequest(rbacUserDTO.getPageNo() - 1, rbacUserDTO.getPageSize());
        Page<RbacUser> rbacUserPage = userService.find(rbacUserDTO, pageRequest);
        return rbacUserPage;

    }

    @GetMapping("/{id}")
    @ApiOperation(value = "根据id获取用户")
    @PreAuthorize("hasAuthority('READ')")
    @ApiImplicitParams({@ApiImplicitParam(name = "jwt-token", value = "jwt-token", required = true, dataType = "string", paramType = "header")})
    public RbacUser getDetail(@PathVariable Long id) {
        return userService.getById(id);
    }

    @GetMapping("/account-by")
    @ApiOperation(value = "根据账号获取用户")
    @PreAuthorize("hasAuthority('READ')")
    @ApiImplicitParams({@ApiImplicitParam(name = "jwt-token", value = "jwt-token", required = true, dataType = "string", paramType = "header")})
    public RbacUser getByAccount(@RequestParam String account) {
        return userService.getByAccount(account);
    }


    @PostMapping("/add")
    @ApiOperation(value = "创建用户")
    @PreAuthorize("hasAuthority('WRITE')")
    @ApiImplicitParams({@ApiImplicitParam(name = "jwt-token", value = "jwt-token", required = true, dataType = "string", paramType = "header")})
    public RbacUser getByAccount(@RequestBody @Validated RbacUserDTO rbacUserDTO) {
        return userService.save(rbacUserDTO);
    }

    @DeleteMapping("/{id}")
    @ApiOperation(value = "删除用户")
    @PreAuthorize("hasRole('ROLE_ADMIN')")
    @Log("删除用户")
    @ApiImplicitParams({@ApiImplicitParam(name = "jwt-token", value = "jwt-token", required = true, dataType = "string", paramType = "header")})
    public void delete(@PathVariable Long id) {
        userService.delete(id);
    }
}

到此已经完成了搭建,上面主要贴出了核心代码,有可能会缺少文件报错。

有兴趣的小伙伴可以下载项目源码:https://blog.csdn.net/HXNLYW/article/details/98037354

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值