SpringBoot整合SpringSecurity

一、添加依赖

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

<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.1</version>
</dependency>
<dependency>
    <groupId>javax.xml.bind</groupId>
    <artifactId>jaxb-api</artifactId>
    <version>2.3.1</version>
</dependency>

二、添加配置文件

2.1、过滤器配置

@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {

    private static final PathMatcher pathmatcher = new AntPathMatcher();


    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain chain) throws ServletException, IOException {

        for (String reg : NoneAuthedResources.BACKEND_RESOURCES) {
            if (pathmatcher.match(reg, request.getServletPath())) {
                chain.doFilter(request, response);
                return;
            }
        }

        try {
            Map<String, Object> claims = JwtUtils.validateTokenAndGetClaims(request);
            String role = String.valueOf(claims.get(ROLE));
            Long userId = Long.valueOf(claims.get("userId") + "");
            SecurityContextHolder.getContext().setAuthentication(
                    new UsernamePasswordAuthenticationToken(userId, null, Arrays.asList(() -> ""))
            );
        } catch (Exception e) {
            ResponseUtils.buildResponse(response, R.response(ResultCodeEnum.NOT_AUTHENTICATION,null), HttpStatus.UNAUTHORIZED);
            return;
        }
        chain.doFilter(request, response);
    }

}

2.2、跨域配置

跨域支持

@Configuration
public class WebConfig implements WebMvcConfigurer {

    /**
     * 跨域支持
     *
     * @param registry
     */
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedOrigins("*")
                .allowCredentials(true)
                .allowedMethods("GET", "POST", "DELETE", "PUT")
                .allowedHeaders("*")
				.maxAge(3600 * 24);
    }
}

2.3、Security配置类

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }

    @Bean
    public HttpFirewall allowUrlEncodedSlashHttpFirewall() {
        StrictHttpFirewall firewall = new StrictHttpFirewall();
        firewall.setAllowUrlEncodedSlash(true);
        return firewall;
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
                .cors()  //允许跨域访问
                .and()
                .authorizeRequests()

                // 配置那些url需要进行校验--所有请求都需要校验"/"
                .antMatchers("/").authenticated()

                //那些请求不需要校验
                .antMatchers(NoneAuthedResources.BACKEND_RESOURCES).permitAll()

                .anyRequest().authenticated() //自定义校验类
                .and()
                .addFilterBefore(new JwtAuthenticationFilter(),
                        UsernamePasswordAuthenticationFilter.class)
                .sessionManagement()
                //关闭session
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS);
    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers(NoneAuthedResources.FRONTEND_RESOURCES);
    }
}

2.4、白名单资源类

/**
 * 不需要身份验证的资源
 *
 * @author fengshuonan
 * @Date 2020/3/1 16:19
 */
public class NoneAuthedResources {

    /**
     * 前端接口资源
     */
    public static final String[] FRONTEND_RESOURCES = {
            // Swagger相关资源
            "/v2/api-docs",
            "/configuration/ui",
            "/swagger-resources/**",
            "/configuration/security",
            "/swagger-ui.html/**",
            "/webjars/**",
            "/druid/**",
    };

    /**
     * 不要权限校验的后端接口资源
     * <p>
     * ANT风格的接口正则表达式:
     * <p>
     * ? 匹配任何单字符<br/>
     * * 匹配0或者任意数量的 字符<br/>
     * ** 匹配0或者更多的 目录<br/>
     */
    public static final String[] BACKEND_RESOURCES = {
            // 登录相关接口放开过滤
            "/login-api/login",
            // 上传图片接口
            "/commonController/pictureUpload",
            // Swagger及druid相关资源
            "/swagger**/**",
            "/webjars/**",
            "/v2/api-docs",
            "/druid",
    };

}

2.5、获取当前登录人配置类

@Slf4j
@Service
public class SecurityUser{

    @Autowired
    private UserMapper userMapper;

    public Long getLoginId() {
        Object userId = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        if (ObjectUtils.isEmpty(userId)){
            throw new RuntimeException("获取登录人失败");
        }
        return Long.valueOf(userId + "");
    }

    /**
     * 获取当前登录人
     *
     * @return
     */
    public User getLoginUser() {
        User user = userMapper.selectById(getLoginId());
        return user;
    }
}

三、添加工具类

3.1、JWT工具类

public class JwtUtils {

    /**
     * 令牌环有效期
     */
    public static final long EXPIRATION_TIME = 48 * 60 * 60 * 1000;

    /**
     * 令牌环密钥
     */
    public static final String SECRET = "qrAVEd537GKHcxHW";

    /**
     * 令牌环头标识
     */
    public static final String TOKEN_PREFIX = "Bearer";

    /**
     * 配置令牌环在http heads中的键值
     */
    public static final String HEADER_STRING = "Authorization";

    /**
     * 自定义字段-角色字段
     */
    public static final String ROLE = "ROLE";

    //生成令牌环
    public static String generateToken(String userRole, Long userId) {
        HashMap<String, Object> map = new HashMap<>();
        map.put(ROLE, userRole);
        map.put("userId", userId);

        String jwt = Jwts.builder()
                .setClaims(map)
                .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
                // 颁发时间
                .setIssuedAt(new Date())
                // 加密算法和密钥
                .signWith(SignatureAlgorithm.HS512, SECRET)
                .compact();
        return TOKEN_PREFIX + " " + jwt;
    }

    //生成令牌环
    public static String generateToken(String userRole, String userId, long exprationtime) {
        HashMap<String, Object> map = new HashMap<>();
        map.put(ROLE, userRole);
        map.put("userId", userId);

        String jwt = Jwts.builder()
                .setClaims(map)
                .setExpiration(new Date(System.currentTimeMillis() + exprationtime))
                .signWith(SignatureAlgorithm.HS512, SECRET)
                .compact();
        return TOKEN_PREFIX + " " + jwt;
    }

    //令牌环校验
    public static Map<String, Object> validateTokenAndGetClaims(HttpServletRequest request) {
        String token = request.getHeader(HEADER_STRING);
        if (Objects.isNull(token) || StringUtils.equals(token, "null")) {
            throw new TokenValidationException("Missing Token");

        } else {
            Map<String, Object> body = Jwts.parser()
                    .setSigningKey(SECRET)
                    .parseClaimsJws(token.replace(TOKEN_PREFIX, ""))
                    .getBody();
            return body;
        }
    }


    static class TokenValidationException extends RuntimeException {
        public TokenValidationException(String msg) {
            super(msg);
        }
    }
}

3.2、http相应工具类

public class ResponseUtils {

    public static void buildResponse(HttpServletResponse response, Object result, HttpStatus httpStatus) throws IOException {
        // 返回JSON
        response.setContentType("application/json;charset=utf-8");
        // 状态码
        response.setStatus(httpStatus.value());
        response.getWriter().write(JSONUtil.toJsonStr(result));
    }
}

四、编写控制器测试

@RequestMapping("/login-api")
public class LoginController {

    @Autowired private UserService userService;
    @Autowired private WebSecurityConfig webSecurityConfig;
    @Autowired private SecurityUser securityUser;

    @ApiOperation("登录")
    @PostMapping("/login")
    public R save(@RequestBody User user) {
    public R login(@RequestBody User user) {
        User userInDb = userService.getOne(new QueryWrapper<User>().eq("username", user.getUsername()));
        if (ObjectUtils.isEmpty(userInDb)){
            return R.fail("账号不存在");
        }
        return R.success();
        String encode = webSecurityConfig.passwordEncoder().encode("123456");
        boolean isMatches = webSecurityConfig.passwordEncoder().matches(user.getPassword(), userInDb.getPassword());

        if (!isMatches){
            return R.fail("账号或密码错误");
        }

        String token = JwtUtils.generateToken("admin", userInDb.getUserId());

        return R.success(
                "登陆成功",
                new HashMap<String, Object>(){{
                    put(JwtUtils.HEADER_STRING, token);
                    put("username", userInDb.getUsername());
                    put("roles", "admin");
                }}
        );
    }

    @ApiOperation("用户信息")
    @PostMapping("/userinfo")
    public R userinfo() {
        return R.success(securityUser.getLoginUser());
    }

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值