随笔 - 478  文章 - 0  评论 - 31  阅读 - 57万 

git源码地址: springboot-security-jwt: springboot整合security以及jwt,详细说明见:https://www.cnblogs.com/excellencesy/p/17010538.html (gitee.com)

简介:springboot框架整合security与jwt实现登录,访问数据鉴权,jwt登出,接口角色控制,接口权限控制

技术:springboot,redis,security,jwt

核心代码:

pom

复制代码
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>com.auth0</groupId>
            <artifactId>java-jwt</artifactId>
            <version>3.10.0</version>
        </dependency>
复制代码

WebSecurityConfig 核心配置类
主要用于:
  指定密码加密方式:BCryptPasswordEncoder
  指定登录鉴权过滤器:JwtLoginFilter
  指定接口访问鉴权过滤器:JwtFilter
  指定查询用户信息的自定义service:UserService

复制代码
package com.auth.auth.config;

import com.auth.auth.constant.JWTConstants;
import com.auth.auth.filter.JwtFilter;
import com.auth.auth.filter.JwtLoginFilter;
import com.auth.auth.service.UserService;
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.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    private UserService userService;

    @Autowired
    public void setUserService(UserService userService) {
        this.userService = userService;
    }

    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder() {
        return new BCryptPasswordEncoder();
    }

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

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests().anyRequest().authenticated().and()
                .addFilterBefore(new JwtLoginFilter(JWTConstants.JWT_LOGIN_URL, authenticationManager()), UsernamePasswordAuthenticationFilter.class)
                .addFilterBefore(new JwtFilter(), UsernamePasswordAuthenticationFilter.class)
                .csrf()
                .disable();
    }
}
复制代码

登录鉴权类

鉴权:这里获取用户名与密码调用指定api即可

鉴权成功:这里需要基于jwt生成token,响应给调用者

鉴权失败:返回错误信息即可

复制代码
package com.auth.auth.filter;

import com.auth.auth.constant.SystemConstants;
import com.auth.auth.entities.User;
import com.auth.auth.util.JwtUtils;
import com.auth.auth.util.ResponseUtil;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;

import javax.servlet.FilterChain;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class JwtLoginFilter extends AbstractAuthenticationProcessingFilter {

    public JwtLoginFilter(String defaultFilterProcessesUrl, AuthenticationManager authenticationManager) {
        super(new AntPathRequestMatcher(defaultFilterProcessesUrl));
        setAuthenticationManager(authenticationManager);
    }

    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse resp) throws AuthenticationException {
        String username = request.getParameter(SystemConstants.LOGIN_INPUT_PARAM_USERNAME);
        String password = request.getParameter(SystemConstants.LOGIN_INPUT_PARAM_PASSWORD);
        UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
        authRequest.setDetails(authenticationDetailsSource.buildDetails(request));
        return this.getAuthenticationManager().authenticate(authRequest);
    }

    @Override
    protected void successfulAuthentication(HttpServletRequest req, HttpServletResponse resp, FilterChain chain, Authentication authentication) {
        User userDetails = (User) authentication.getPrincipal();
        String jwt = JwtUtils.getInstance().generateToken(userDetails.getUsername(), userDetails.getRole(), userDetails.getPermissions());
        ResponseUtil.success(resp, jwt);
    }

    @Override
    protected void unsuccessfulAuthentication(HttpServletRequest req, HttpServletResponse resp, AuthenticationException failed) {
        ResponseUtil.fail(resp);
    }

}
复制代码

接口访问鉴权

拦截不在白名单的所有接口

获取请求头里的jwtToken并校验其是否可用

复制代码
package com.auth.auth.filter;

import com.auth.auth.constant.AuthExceptionMessage;
import com.auth.auth.constant.JWTConstants;
import com.auth.auth.util.JwtUtils;
import com.auth.auth.util.ResponseUtil;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.web.filter.GenericFilterBean;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;

public class JwtFilter extends GenericFilterBean {

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) servletRequest;
        String jwtToken = req.getHeader(JWTConstants.JWT_REQUEST_HEADER_KEY);
        try {
            UserDetails user = JwtUtils.getInstance().decode(jwtToken);
            UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(user.getUsername(), null, user.getAuthorities());
            SecurityContextHolder.getContext().setAuthentication(token);
        } catch (Exception e) {
            e.printStackTrace();
            ResponseUtil.responseJson(servletResponse, AuthExceptionMessage.INVALID_TOKEN);
        }
        filterChain.doFilter(req, servletResponse);
    }

}
复制代码

自定义用户信息查询service

package com.auth.auth.service;

import org.springframework.security.core.userdetails.UserDetailsService;

public interface UserService extends UserDetailsService {

}
复制代码
package com.auth.auth.service.impl;

import com.auth.auth.entities.User;
import com.auth.auth.service.UserService;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

@Service
public class UserServiceImpl implements UserService {

    @Override
    public UserDetails loadUserByUsername(String userName) throws UsernameNotFoundException {
        User user = new User();
        user.setUsername(userName);
        user.setPassword("$2a$10$Zr6M2iK41CbW0wKLweuilu7PGaCgCF24TD16w7IvTiNkcRE1ZlxM.");//123456
        user.setId("1");
        user.setRole("dev");
        String permissions = "auth:test:test1,ROLE_" + user.getRole();
        user.setPermissions(permissions);
        return user;
    }
}
复制代码

jwttoken生成与校验工具类

复制代码
package com.auth.auth.util;

import com.auth.auth.constant.JWTConstants;
import com.auth.auth.entities.User;
import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import org.joda.time.DateTime;
import org.springframework.stereotype.Component;

import java.util.Map;

@Component
public class JwtUtils {

    private static final JwtUtils jwtUtils = new JwtUtils();

    private JwtUtils() {
    }

    public static JwtUtils getInstance() {
        return jwtUtils;
    }

    Algorithm getAlgorithm() {
        return Algorithm.HMAC256(JWTConstants.JWT_KEY);
    }

    public String generateToken(String userName, String userRole, String userPermission) {
        return JWT.create()
                .withClaim(JWTConstants.JWT_KEY_USER_NAME, userName)
                .withClaim(JWTConstants.JWT_KEY_ROLE, userRole)
                .withClaim(JWTConstants.JWT_KEY_PERMISSION, userPermission)
                .withExpiresAt(DateTime.now().plusDays(JWTConstants.EXPIRE_DAY).toDate())
                .sign(getAlgorithm());
    }

    public User decode(String token) {
        DecodedJWT decodedJWT = JWT.require(getAlgorithm()).build().verify(token);
        Map<String, Claim> jwt = decodedJWT.getClaims();
        String userName = jwt.get(JWTConstants.JWT_KEY_USER_NAME).asString();
        String userRole = jwt.get(JWTConstants.JWT_KEY_ROLE).asString();
        String userPermission = jwt.get(JWTConstants.JWT_KEY_PERMISSION).asString();
        return new User(userName, userRole, userPermission);
    }

}
复制代码

编写测试接口

使用权限注解需要加注解@EnableGlobalMethodSecurity(prePostEnabled = true)(我加在了WebSecurityConfig类上了)

复制代码
package com.auth.auth.controller;

import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/test")
public class TestController {

    @PreAuthorize("hasPermission('auth','auth:test:test1')")
    @RequestMapping("test1")
    public String test() {
        return "test1::success";
    }

    @PreAuthorize("hasRole('dev')")
    @RequestMapping("test2")
    public String test3() {
        return "test2::success";
    }

    @RequestMapping("test3")
    public String test4() {
        return "test3::success";
    }

}
复制代码
hasPermission注解也需要自定义处理逻辑
复制代码
package com.auth.auth.util;

import org.springframework.security.access.PermissionEvaluator;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.stereotype.Component;

import java.io.Serializable;
import java.util.Set;
import java.util.stream.Collectors;

@Component
public class CustomPermissionEvaluator implements PermissionEvaluator {
    @Override
    public boolean hasPermission(Authentication authentication, Object accessType, Object permission) {
        if (authentication != null && authentication.getAuthorities() != null) {
            Set<String> permissionSet = authentication.getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.toSet());
            if (accessType instanceof String) {
                return permissionSet.contains(permission);
            }
        }
        return false;
    }

    @Override
    public boolean hasPermission(Authentication authentication, Serializable serializable, String targetType, Object permission) {
        return hasPermission(authentication, targetType, permission);
    }

}
复制代码

测试

登录:

 

 访问数据接口

 访问限制角色的数据接口

 访问限制权限的数据接口

 

 

 本文章只展示核心代码,完整代码见git,文首有git地址

 

posted on   song.yan  阅读(216)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示