SpringBoot使用security和jwt进行鉴权设计

本文档详细介绍了如何使用Spring Boot、Spring Security和JWT实现用户登录、生成令牌以及权限验证。通过创建用户实体、JWT过滤器、服务和配置安全设置,实现了登录接口、令牌管理和权限校验功能。同时,提供了Swagger2接口文档配置,以便测试和文档化API。
摘要由CSDN通过智能技术生成

一、使用security默认登录接口登录成功、生成token、返回前段

项目的结构如下:

1.引入jar包

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

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <!-- alibaba json -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.41</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <!-- JWT -->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.7.0</version>
        </dependency>

        <!--swagger相关-start-->
        <dependency>
            <groupId>io.swagger</groupId>
            <artifactId>swagger-annotations</artifactId>
            <version>1.5.19</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>2.9.2</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>2.9.2</version>
        </dependency>
        <!--swagger相关-end-->

测试的用户实体

@Data
public class User {

    private String id;

    private String name;

    private String password;

    private boolean dev;

    private boolean channel;

    public User() {
    }

    public User(String id, String name, String password) {
        this.id = id;
        this.name = name;
        this.password = password;
    }
}

 Jwt生成token,拦截和验证token的合法性

package com.example.security.Jwt;

import com.example.security.token.TokenManager;
import com.example.security.util.SpringContextUtil;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
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.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * Created by zhicheng.zhao on 2020/9/4.
 */
public class JwtAuthFilter extends BasicAuthenticationFilter {
    private TokenManager tokenManager;

    public JwtAuthFilter(AuthenticationManager authenticationManager, TokenManager tokenManager) {
        super(authenticationManager);
        this.tokenManager = tokenManager;
    }

    /**
     * 过滤器
     *
     * @param request
     * @param response
     * @param chain
     * @throws IOException
     * @throws ServletException
     */
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
        // 设置请求权限
        response.setHeader("Access-Control-Allow-Origin", request.getHeader("origin"));
        response.setHeader("Access-Control-Allow-Credentials", "true");
        response.setHeader("Access-Control-Allow-Methods", "PUT,POST,GET,OPTIONS,DELETE,PATCH");
        response.setHeader("Access-Control-Allow-Headers", "tokenId,userType,token,Origin,X-Requested-With,Content-Type,Accept,appKey,hszCookie");
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json,charset=utf-8");

        String url = request.getRequestURI();

        // 鉴权判断:本地环境模拟管理员权限,其他环境需要根据url鉴权 zzc
        System.out.println(SpringContextUtil.getProp("authVerify"));
        if (SpringContextUtil.getProp("authVerify")==null?false:SpringContextUtil.getProp("authVerify").equalsIgnoreCase("false1")) {
            // 本地环境,存储用户认证信息
            String tokenTmp = tokenManager.createTokenAll("test");
            UsernamePasswordAuthenticationToken authenticationToken = getAuthentication(tokenTmp);
            SecurityContextHolder.getContext().setAuthentication(authenticationToken);

            chain.doFilter(request, response);

            return;
        } else if (urlFilter(url)) {
            chain.doFilter(request, response);

            return;
        }

        // 过滤token为空
        String token = request.getHeader("token");
        if (token == null || "".equals(token)) {
            response.setHeader("Content-Type", "application/json");
            response.getWriter().print("token为空!");

            return;
        }

        // 从token获取用户认证信息
        UsernamePasswordAuthenticationToken authenticationToken = getAuthentication(token);
        if (authenticationToken == null) {

            response.setHeader("Content-Type", "application/json");
            response.getWriter().print("token信息错误!");
            return;
        }

        // 存储用户认证信息
        SecurityContextHolder.getContext().setAuthentication(authenticationToken);

        chain.doFilter(request, response);
    }

    /**
     * 不需要token的地址:
     * 用户名登录/手机登录/第三方登录/微信登录
     * 注册
     * 登录前(authFree)
     *
     * @param url
     * @return
     */
    private boolean urlFilter(String url) {
        return url.contains("/login")
                || url.contains("Login")
                || url.contains("/regist")
                || url.contains("Regist")
                || url.contains("/authFree");
    }

    /**
     * 从token获取用户认证信息
     *
     * @param token
     * @return
     */
    private UsernamePasswordAuthenticationToken getAuthentication(String token) {
        try {
            Claims claims = Jwts.parser()
                    .setSigningKey("JwtSecret")
                    .parseClaimsJws(token)
                    .getBody();
            String user = claims.getSubject();
            String role = claims.get("roles").toString();

            List<SimpleGrantedAuthority> userRolesByToken = Arrays.stream(role.split(","))
                    .map(SimpleGrantedAuthority::new)
                    .collect(Collectors.toList());

            if (!user.isEmpty()) {
                return new UsernamePasswordAuthenticationToken(user, null, userRolesByToken);
            }
        } catch (Exception e) {
            logger.info(e.getMessage());
        }
        return null;
    }
}

 查询数据库用户信息的服务

@Service
public class UserService {

    /**
    * @Description: 这里应该写的是调用Dao层,根据主键查询出用户,由于我没有配置直接返回用户,注意,密码是经过加密的
    * @author: YunZhao.Wang
    * @date: 2020/10/21 10:26
    * @version:
    */

    public User getUser(String name){

       User user =  new User("1","test", new BCryptPasswordEncoder().encode("123")) ;
       if (user.getName().equals(name)){
           return user;
       }else {
           return new User();
       }
    }
}

 实现security底层查询用户信息的接口,让security的底层查询数据的用户信息

@Service
public class CustomUserService implements UserDetailsService {

    @Autowired
    private UserService userService;

    @Override
    public UserDetails loadUserByUsername(String account) throws UsernameNotFoundException {
        //账号信息
        com.example.security.model.User accountInfo = userService.getUser(account);
        if (accountInfo == null)
            throw new UsernameNotFoundException("Account[" + account + "]not found");

        List<SimpleGrantedAuthority> authorities = new ArrayList<>();
        //对应的权限添加
        authorities.add(new SimpleGrantedAuthority("ROLE_USER"));

        return new User(accountInfo.getName(), accountInfo.getPassword(), authorities);

    }
}

集成 WebSecurityConfigurerAdapter,重写configure接口

package com.example.security.security;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.example.security.Jwt.JwtAuthFilter;
import com.example.security.model.User;
import com.example.security.token.TokenManager;
import org.springframework.beans.BeanUtils;
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.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.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

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

/**
 * @program: security进行登录
 * @description: Security
 * @author: YunZhao.Wang
 * @create: 2020-10-21 10:29
 **/
@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private CustomUserService customUserService;

    @Autowired
    private TokenManager tokenManager;

    @Override
    public void configure(HttpSecurity http) throws Exception {
        //关闭csrf保护
        http.csrf().disable();

        // 无状态
        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);

        // 添加filter
        http.addFilterBefore(new JwtAuthFilter(authenticationManager(), tokenManager), UsernamePasswordAuthenticationFilter.class);

        //登陆
        http.formLogin().successHandler(loginSuccessHandler()).failureHandler(loginFailureHandler())
                .permitAll();

        // 无拦截
        http.authorizeRequests().anyRequest().permitAll();

        //退出
        http.logout().logoutSuccessUrl("/login")
                .permitAll();
    }

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {

        auth.userDetailsService(customUserService).passwordEncoder(passwordEncoder());

    }

    /**
     * 密码加密
     *
     * @return
     */
    @Bean
    public BCryptPasswordEncoder passwordEncoder() {
        //密码加密
        return new BCryptPasswordEncoder(4);
    }

    /**
     * 登录成功
     *
     * @return
     */
    @Bean
    AuthenticationSuccessHandler loginSuccessHandler() {
        return new AuthenticationSuccessHandler() {

            @Autowired
            private UserService userService;

            @Override
            public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication auth) throws IOException, ServletException {
                String account = request.getParameter("username");
                String token = tokenManager.createTokenBusiness(account);
                response.setCharacterEncoding("UTF-8");
                response.setContentType("application/json,charset=utf-8");
                response.getWriter().print("登录成功!token:" + token);
            }
        };
    }

    /**
     * 登录失败
     *
     * @return
     */
    @Bean
    AuthenticationFailureHandler loginFailureHandler() {
        return new AuthenticationFailureHandler() {
            @Override
            public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {

//                Result result = new Result(ExceptionEnum.LOGIN_ERROR.getErrorCode(), ExceptionEnum.LOGIN_ERROR.getErrorMsg());
//                response.setHeader("Content-Type", "application/json");
                response.getWriter().print("登录失败!");
            }
        };
    }

    /**
     * 静态资源配置
     *
     * @param web
     * @throws Exception
     */
    @Override
    public void configure(WebSecurity web) {
        web.ignoring().antMatchers("/swagger-ui.html")
                .antMatchers("/webjars/**")
                .antMatchers("/v2/**")
                .antMatchers("/swagger-resources/**");
    }
}

token工具类 

package com.example.security.token;


import com.example.security.model.User;
import com.example.security.security.UserService;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * Created by zhangnianzhong on 2019/8/27.
 */
@Component("tokenManager")
public class TokenManager {
    private static Logger logger = LoggerFactory.getLogger(TokenManager.class);

    @Autowired
    private UserService accountService;



    public String createToken(String userId) {
        long expire_time = 1000 * 60 * 60 * 12;
        String token = Jwts.builder()
                .setSubject(userId)
                .setExpiration(new Date(System.currentTimeMillis() + expire_time))
                .signWith(SignatureAlgorithm.HS512, "JwtSecret")
                .compact();
        return token;
    }

    /**
     * B端用户权限
     *
     * @param userName
     * @return
     */
    public String createTokenBusiness(String userName) {
        long expireTime = 1000 * 60 * 60 * 12; //过期时间为12小时

        User account = accountService.getUser(userName);
        List<String> list= new ArrayList<>();
        //因为用户没有设置管理权限,故默认都是ROLE_00权限,在接口加注解包含00,即是管理员权限的接口
        if (account.isDev()) {
            list.add("ROLE_01");
        }
        if (account.isChannel()) {
            list.add("ROLE_02");
        }
        if (!account.isChannel() && !account.isDev()) {
            list.add("ROLE_00");
        }

        String token = Jwts.builder()
                .signWith(SignatureAlgorithm.HS512, "JwtSecret")
                .claim("roles", String.join(",", list))
                .setSubject(userName)
                .setExpiration(new Date(System.currentTimeMillis() + expireTime))
                .compact();

        return token;
    }


    /**
     * 模拟所有用户权限
     *
     * @param userName
     * @return
     */
    public String createTokenAll(String userName) {
        long expireTime = 1000 * 60 * 60 * 12;

        List<String> list= new ArrayList<>();
        list.add("ROLE_00");
        list.add("ROLE_01");
        list.add("ROLE_02");
        list.add("ROLE_10");
        list.add("ROLE_11");

        String token = Jwts.builder()
                .signWith(SignatureAlgorithm.HS512,"JwtSecret")
                .claim("roles", String.join(",", list))
                .setSubject(userName)
                .setExpiration(new Date(System.currentTimeMillis() + expireTime))
                .compact();

        return token;
    }
    public String getRoles(String token) {
        try {
            Claims claims = Jwts.parser()
                    .setSigningKey("JwtSecret")
                    .parseClaimsJws(token)
                    .getBody();
            String roles = claims.get("roles").toString();

            return roles;
        } catch (Exception e) {
            logger.info(e.getMessage());
        }
        return null;
    }
}

获取当前的环境

@Component
public class SpringContextUtil implements ApplicationContextAware {
    private static ApplicationContext context = null;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext)
            throws BeansException {
        this.context = applicationContext;
    }

    // 传入线程中
    public static <T> T getBean(String beanName) {
        return (T) context.getBean(beanName);
    }

    // 国际化使用
    public static String getMessage(String key) {
        return context.getMessage(key, null, Locale.getDefault());
    }

    /// 获取当前环境
    public static String getActiveProfile() {
        return context.getEnvironment().getActiveProfiles()[0];
    }

    /// 获取当前环境
    public static String getProp(String prop) {
        return context.getEnvironment().getProperty(prop);
    }
}

swagger配置文件

@Configuration
@EnableSwagger2
//@Profile({"test","dev","prod"})
public class Swagger2 {

    @Bean
    public Docket createRestApi() {
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.example.security.provider.controller"))
                .paths(PathSelectors.any())
                .build()
                .globalOperationParameters(setHeaderToken());
    }

    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title("权限版本二-V1-APIs")
                .description("")
                .termsOfServiceUrl("")
                .version("1.0")
                .build();
    }
    // 添加token验证
    private List<Parameter> setHeaderToken() {
        ParameterBuilder tokenPar = new ParameterBuilder();
        List<Parameter> pars = new ArrayList<>();
        tokenPar.name("token").description("token").modelRef(new ModelRef("string")).parameterType("header").required(false).build();
        pars.add(tokenPar.build());
        return pars;
    }
}

 controller接口

@RestController
@Api(tags = {"用户管理"},description = "带接口鉴权的用户管理接口")
public class UserController {
    @PreAuthorize("hasAnyRole('00')")
    @GetMapping("getUser")
    @ApiOperation(value = "获取用户",notes = "获取用户")
    public User getUser(){
        return new User("3","ceshi","789");
    }
}

配置文件

server.port= 10010

之后我们可以使用postman进行测试,使用security自带的login接口,是psot请求

 

二、请求头带上token访问后端接口

之后我们使用返回的token访问获取用户的接口,打开swagger接口(http://localhost:10010/swagger-ui.html),

因为默认都是00的权限,登录我是写死的,返回的一定也是00的权限,为了测试,我们修改获取用户借口中的注解参数

 

再次访问,出现403错误,无权限访问,现在的token是非00生成的,所以只能权限为非00的访问,这个返回结果我没有定义全局的异常处理,大家可以对异常进行再一次的封装

实例的连接

链接: https://pan.baidu.com/s/10ibBRxHEWakoENNBsvRALA 提取码: cd7u 复制这段内容后打开百度网盘手机App,操作更方便哦

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值