SpringBoot使用security进行登录验证

话不多说,直接搞代码

1.在pom.xml添加依赖

        <!--        securityr认证-->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-security</artifactId>
		</dependency>
		<dependency>
			<groupId>io.jsonwebtoken</groupId>
			<artifactId>jjwt</artifactId>
			<version>0.9.0</version>
		</dependency>
		<dependency>
			<groupId>javax.xml.bind</groupId>
			<artifactId>jaxb-api</artifactId>
			<version>2.3.1</version>
		</dependency>
		<!--json-->
		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>fastjson</artifactId>
			<version>1.2.75</version>
		</dependency>

2.在config包新建SecurityConfig.java文件,新建auth包,在auth包里新建以下文件

AuthenticationBean.java
@Data
public class AuthenticationBean {
    private String username;
    private String password;
}
AuthResult.java

public enum AuthResult implements EnumInterface {

    SUCCESS(0, "登录成功"),
    UNAUTHORIZED(401, "身份验证失败"),
    FORBIDDEN(403, "权限验证失败"),
    USER_NOT_EXIST(407, "用户不存在"),
    USER_PASSWORD_ERR(408, "密码不正确"),
    ;

    private final Integer code;

    private final String message;

    AuthResult(Integer code, String message) {
        this.code = code;
        this.message = message;
    }

    @Override
    public Integer getCode() {
        return this.code;
    }

    @Override
    public String getMessage() {
        return this.message;
    }
}
CustomAuthenticationFilter.java
@Slf4j
public class CustomAuthenticationFilter extends UsernamePasswordAuthenticationFilter {
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {

        log.info("登录拦截");
        //attempt Authentication when Content-Type is json

        //use jackson to deserialize json
        ObjectMapper mapper = new ObjectMapper();
        UsernamePasswordAuthenticationToken authRequest = null;
        try (InputStream is = request.getInputStream()) {
            AuthenticationBean authenticationBean = mapper.readValue(is, AuthenticationBean.class);
            log.info(authenticationBean.toString());
            authRequest = new UsernamePasswordAuthenticationToken(
                    authenticationBean.getUsername(), authenticationBean.getPassword());
        } catch (IOException e) {
            e.printStackTrace();
            new UsernamePasswordAuthenticationToken(
                    "", "");
        } finally {
            setDetails(request, authRequest);
            return this.getAuthenticationManager().authenticate(authRequest);
        }
        //transmit it to UsernamePasswordAuthenticationFilter

    }

}
JwtAccessDeniedHandler.java

@Slf4j
@Component
public class JwtAccessDeniedHandler implements AccessDeniedHandler {
    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
        log.info("权限异常处理");
        response.setStatus(HttpServletResponse.SC_FORBIDDEN);
        Map map = new HashMap();
        map.put("code", AuthResult.FORBIDDEN.getCode());
        map.put("message", AuthResult.FORBIDDEN.getMessage());
        String json = JSON.toJSONString(map);
        response.setContentType("text/json;charset=utf-8");
        response.getWriter().write(json);
    }
}
JwtAuthenticationEntryPoint.java
@Slf4j
@Component
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {
    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException {
        log.info("身份验证失败");
        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
        Map map = new HashMap<>();
        map.put("code", AuthResult.UNAUTHORIZED.getCode());
        map.put("message", AuthResult.UNAUTHORIZED.getMessage());
        String json = JSON.toJSONString(map);
        response.setContentType("text/json;charset=utf-8");
        response.getWriter().write(json);
    }
JwtAuthenticationTokenFilter.java
@Component
@Slf4j
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {

    @Autowired
    private UserDetailsService userService;

    @Autowired
    private JwtToken jwtToken;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        String requestUrl = request.getRequestURI();
        log.info(requestUrl);
        String authToken = request.getHeader("Authorization");
        if (authToken != null) {
            authToken = authToken.replace("Bearer ", "");
        }
        log.info(authToken);
        String username = jwtToken.getUsernameFromToken(authToken);
        log.info("token 验证 {}", username);
        if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
            UserDetails userDetails = this.userService.loadUserByUsername(username);
            if (jwtToken.validateToken(authToken, userDetails)) {
                UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
                authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                // 将 authentication 存入 ThreadLocal,方便后续获取用户信息
                SecurityContextHolder.getContext().setAuthentication(authentication);
            }
        }
        filterChain.doFilter(request, response);
    }
}

JwtToken.java
@Data
@ConfigurationProperties(prefix = "jwt")
@Component
@Slf4j
public class JwtToken {
    // 过期时间 毫秒
    private Long expiration;
    private String secret;
    private String header;


    /**
     * 从数据声明生成令牌
     *
     * @param claims 数据声明
     * @return 令牌
     */
    private String generateToken(Map<String, Object> claims) {
        log.info(expiration.toString());
        log.info(secret);
        Date expirationDate = new Date(System.currentTimeMillis() + expiration);
        return Jwts.builder().setClaims(claims).setExpiration(expirationDate).signWith(SignatureAlgorithm.HS256, secret).compact();
    }

    /**
     * 从令牌中获取数据声明
     *
     * @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;
    }

    public Date getExpiration(String token) {
        Claims claims = this.getClaimsFromToken(token);
        Date date = claims.getExpiration();
        return date;
    }

    /**
     * 生成令牌
     *
     * @param userDetails 用户
     * @return 令牌
     */
    public String generateToken(UserDetails userDetails) {
        Map<String, Object> claims = new HashMap<>(2);
        JwtUser jwtUser = (JwtUser) userDetails;
        String token = jwtUser.getUsername();
        if (jwtUser.getOther() != null) {
            token = token + "|" + jwtUser.getOther();
        }
        claims.put(Claims.SUBJECT, token);
        claims.put(Claims.ISSUED_AT, new Date());
        return generateToken(claims);
    }

    /**
     * 从令牌中获取用户名
     *
     * @param token 令牌
     * @return 用户名
     */
    public String getUsernameFromToken(String token) {
        String username;
        try {
            Claims claims = getClaimsFromToken(token);
            username = claims.getSubject();
        } catch (Exception e) {
            username = null;
        }
        return username;
    }

    public String getUserId(String token) {
        String username;
        try {
            Claims claims = getClaimsFromToken(token);
            username = claims.getSubject();
        } catch (Exception e) {
            username = null;
        }
        return username;
    }

    /**
     * 判断令牌是否过期
     *
     * @param token 令牌
     * @return 是否过期
     */
    public Boolean isTokenExpired(String token) {
        try {
            Claims claims = getClaimsFromToken(token);
            Date expiration = claims.getExpiration();
            return expiration.before(new Date());
        } catch (Exception e) {
            return true;
        }
    }

    /**
     * 刷新令牌
     *
     * @param token 原令牌
     * @return 新令牌
     */
    public String refreshToken(String token) {
        String refreshedToken;
        try {
            Claims claims = getClaimsFromToken(token);
            claims.put(Claims.ISSUED_AT, new Date());
            refreshedToken = generateToken(claims);
        } catch (Exception e) {
            refreshedToken = null;
        }
        return refreshedToken;
    }

    /**
     * 验证令牌
     *
     * @param token       令牌
     * @param userDetails 用户
     * @return 是否有效
     */
    public Boolean validateToken(String token, UserDetails userDetails) {
        String username = getUsernameFromToken(token);
        return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
    }

}
JwtUser.java
@NoArgsConstructor
@Setter
@Component
public class JwtUser implements UserDetails {

    private Integer id;

    private String username;

    private String password;

    private String name;

    private String other;

    private Integer roleId;

    private Collection<? extends GrantedAuthority> authorities;

    public String getOther() {
        return this.other;
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return authorities;
    }

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

    public Integer getId() {
        return id;
    }

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

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setRoleId(Integer roleId) {
        this.roleId = roleId;
    }

    public Integer getRoleId() {
        return roleId;
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return true;
    }
}

LoginFailureHandler.java
@Slf4j
@Component
public class LoginFailureHandler implements AuthenticationFailureHandler {
    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
        log.info("登录失败");
        // 使用fastjson
        // 指定响应格式是json
        Map map = new HashMap();
        if (exception.getClass() == UsernameNotFoundException.class) {
            map.put("code", AuthResult.USER_NOT_EXIST.getCode());
            map.put("message", AuthResult.USER_NOT_EXIST.getMessage());
        } else {
            map.put("code", AuthResult.USER_PASSWORD_ERR.getCode());
            map.put("message", AuthResult.USER_PASSWORD_ERR.getMessage());
        }
        String json = JSON.toJSONString(map);
        response.setContentType("text/json;charset=utf-8");
        response.getWriter().write(json);
    }
}
MyAuthenticationSuccessHandler.java
@Slf4j
@Component
public class MyAuthenticationSuccessHandler implements AuthenticationSuccessHandler {

    @Autowired
    private JwtToken jwtToken;

    @Override
    public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
        log.info("登录成功");
        log.info(authentication.getName());
        JwtUser userDetails = (JwtUser) authentication.getPrincipal();
        log.info(JSON.toJSONString(userDetails));

        String token = jwtToken.generateToken(userDetails);
        Claims claims = jwtToken.getClaimsFromToken(token);
        Date expiration = claims.getExpiration();

        UserSimple userSimple = new UserSimple();
        BeanUtils.copyProperties(userDetails, userSimple);

        Map tokenMap = new HashMap();
        tokenMap.put("token", token);
        tokenMap.put("expiration", expiration);
        Map result = new HashMap();

        Map data = new HashMap();
        data.put("user", userSimple);
        data.put("token", tokenMap);

        result.put("data", data);
        result.put("code", AuthResult.SUCCESS.getCode());
        result.put("message", "登录成功");

        SecurityContextHolder.getContext().setAuthentication(authentication);
        String json = JSON.toJSONString(result);
        response.setContentType("text/json;charset=utf-8");
        response.getWriter().write(json);
    }
}

UserDetailsServiceImpl.java
@Service
@Slf4j
public class UserDetailsServiceImpl implements UserDetailsService {

    @Autowired
    private UserService userService;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
        log.info(username);
        userQueryWrapper.eq("username", username);
        User user = this.userService.getOne(userQueryWrapper);
        if (user == null) {

            log.info(String.format("%s.这个用户不存在", username));
            throw new UsernameNotFoundException(String.format("%s.这个用户不存在", username));
        }
        log.info("加载用户信息");
        List<Integer> rolesId = new ArrayList<>();

        List<GrantedAuthority> grantedAuthorities = new ArrayList<>();
        JwtUser jwtUser = new JwtUser();
        jwtUser.setName(user.getName());
        jwtUser.setId(user.getId());
        jwtUser.setUsername(user.getUsername());
        jwtUser.setPassword(user.getPassword());
        jwtUser.setAuthorities(grantedAuthorities);
        return jwtUser;
    }
}
UserSimple.java
@Data
public class UserSimple {
    private Integer id;
    private String username;
    private String name;
}

文件目录如下

 3.在SwaggerConfig.java类中加入Security授权信息

SwaggerConfig.java

/**
 * <p>
 *
 * </p>
 *
 * @author Qin Xiaotian
 * @since 2021-8-4
 */
@Component
@ConfigurationProperties("swagger")
@Data
public class SwaggerConfig {
    private final SwaggerProperties swaggerProperties;

    public SwaggerConfig(SwaggerProperties swaggerProperties) {
        this.swaggerProperties = swaggerProperties;
    }

    @Bean
    public Docket createRestApi() {
        return new Docket(DocumentationType.OAS_30).pathMapping("/")


                // 定义是否开启swagger,false为关闭,可以通过变量控制
                .enable(swaggerProperties.getEnable())


                // 将api的元信息设置为包含在json ResourceListing响应中。
                .apiInfo(apiInfo())


                // 接口调试地址
                .host(swaggerProperties.getTryHost())


                // 选择哪些接口作为swagger的doc发布
                .select()
                .apis(RequestHandlerSelectors.any())
                .paths(PathSelectors.any())
                .build()


                // 支持的通讯协议集合
                .protocols(newHashSet("https", "http"))


                // 授权信息设置,必要的header token等认证信息
                .securitySchemes(securitySchemes())


                // 授权信息全局应用
                .securityContexts(securityContexts());

    }

    /**
     * API 页面上半部分展示信息
     */
    private ApiInfo apiInfo() {
        return new ApiInfoBuilder().title(swaggerProperties.getApplicationName() + " Api Doc")
                .description(swaggerProperties.getApplicationDescription())
                .version("Application Version: " + swaggerProperties.getApplicationVersion() + ", Spring Boot Version: " + SpringBootVersion.getVersion())
                .build();
    }

    /**
     * 设置授权信息
     */
    private List<SecurityScheme> securitySchemes() {
        ApiKey apiKey = new ApiKey("Authorization", "Authorization", In.HEADER.toValue());
        return Collections.singletonList(apiKey);
    }


    /**
     * 授权信息全局应用
     */
    private List<SecurityContext> securityContexts() {
        return Collections.singletonList(
                SecurityContext.builder()
                        .securityReferences(Collections.singletonList(new SecurityReference("Authorization", new AuthorizationScope[]{new AuthorizationScope("global", "")})))
                        .build()
        );
    }


    @SafeVarargs
    private final <T> Set<T> newHashSet(T... ts) {
        if (ts.length > 0) {
            return new LinkedHashSet<>(Arrays.asList(ts));
        }
        return null;
    }

}

4.测试

运行springBoot项目,在右上角多了一个Authorize (授权)按钮

直接访问接口,如果没有经过登录授权,是无法访问

 

通过PostMan登录获取 token,然后使用生成的token信息进行登录授权

 

 授权之后,就可以访问所有接口信息(注:每次刷新页面,需要重新授权

 


版权声明:本文为qq_28503457原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。