GVKun编程网logo

在Spring Security UsernamePasswordAuthenticationFilter JWT身份验证中设置自定义登录URL

16

针对在SpringSecurityUsernamePasswordAuthenticationFilterJWT身份验证中设置自定义登录URL这个问题,本篇文章进行了详细的解答,同时本文还将给你拓展d

针对在Spring Security UsernamePasswordAuthenticationFilter JWT身份验证中设置自定义登录URL这个问题,本篇文章进行了详细的解答,同时本文还将给你拓展day76(Spring Security + JWT,JwtAuthenticationFilter)、java – 自定义authenticationFilter Spring Security 3.2、java – 配置Spring Security以使用customPasswordAuthenticationFilter、JwtUser JwtAuthenticationEntryPoint JwtAuthorizationTokenFilter JwtUserDetailsService等相关知识,希望可以帮助到你。

本文目录一览:

在Spring Security UsernamePasswordAuthenticationFilter JWT身份验证中设置自定义登录URL

在Spring Security UsernamePasswordAuthenticationFilter JWT身份验证中设置自定义登录URL

我正在按照auth0的教程使用JWT保护我的应用程序。

我完成了以下WebSecurity配置:

@EnableWebSecurity@AllArgsConstructor(onConstructor = @__(@Autowired))public class WebSecurity extends WebSecurityConfigurerAdapter {    private final UserDetailsService userDetailsService;    private final BCryptPasswordEncoder passwordEncoder;    @Override    protected void configure(HttpSecurity http) throws Exception {        http.formLogin()                .and().cors()                .and().csrf()                .disable()                .authorizeRequests()                .antMatchers(HttpMethod.POST, REGISTER_URL).permitAll()                .antMatchers(HttpMethod.POST, LOGIN_URL).permitAll()                .anyRequest().authenticated()                .and()                .addFilter(new JWTAuthorizationFilter(authenticationManager()))                .addFilter(new JWTAuthenticationFilter(authenticationManager()))                // This disables session creation on Spring Security                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);    }    @Override    protected void configure(AuthenticationManagerBuilder auth) throws Exception {        auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder);    }    @Bean    public CorsConfigurationSource corsConfigurationSource() {        final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();        source.registerCorsConfiguration("/**", new CorsConfiguration().applyPermitDefaultValues());        return source;    }}

和以下JWTAuthenticationFilter:

public class JWTAuthenticationFilter extends UsernamePasswordAuthenticationFilter {    private final AuthenticationManager authenticationManager;    public JWTAuthenticationFilter(AuthenticationManager authenticationManager) {        this.authenticationManager = authenticationManager;    }    @Override    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException {        try {            ApplicationUser credentials = new ObjectMapper().readValue(request.getInputStream(), ApplicationUser.class);            return authenticationManager.authenticate(                    new UsernamePasswordAuthenticationToken(                            credentials.getUsername(),                            credentials.getPassword(),                            new ArrayList<>()                    )            );        } catch (IOException e) {            throw new RuntimeException(e);        }    }    @Override    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {        String token = Jwts.builder()                .setSubject(((User) authResult.getPrincipal()).getUsername())                .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))                .signWith(SignatureAlgorithm.HS512, SECRET.getBytes())                .compact();        response.addHeader(HEADER_STRING, TOKEN_PREFIX + token);    }}

目前,该应用接受/loginURL
上的POST请求。我想知道如何将URL更改为/api/auth/login。有什么方法可以将URL字符串注入到身份验证过滤器中,或以某种方式在安全配置中对其进行设置?

答案1

小编典典

您正在扩展org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter,它本身又扩展了
org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter。在最后一节课中,有一个名为setter的操作setFilterProcessesUrl,它旨在执行以下操作:

setFilterProcessesUrl

public void setFilterProcessesUrl (字符串filterProcessesUrl)

设置确定是否需要身份验证的URL

参数:filterProcessesUrl

这是该javadoc部分的链接

因此,您WebSecurityConfigurerAdapter可以像这样:

@Beanpublic JWTAuthenticationFilter getJWTAuthenticationFilter() {    final JWTAuthenticationFilter filter = new JWTAuthenticationFilter(authenticationManager());    filter.setFilterProcessesUrl("/api/auth/login");    return filter;}

然后在configure同一个类的方法中,只需引用它即可,而不是创建新实例:

.addFilter(getJWTAuthenticationFilter())

day76(Spring Security + JWT,JwtAuthenticationFilter)

day76(Spring Security + JWT,JwtAuthenticationFilter)

day76(Spring Security + JWT,JwtAuthenticationFilter)

Spring Security + JWT

1.将返回的数据改为JWT数据

2.JWT组成

1.Header(头):指定算法和当前数据类型

2.Payload(载荷):Claims(自定义数据)和过期时间

3.Signature(签名):算法和密钥

3.代码

@Service
public class AdminServiceImpl implements IAdminService {

   // ===== 原有其它代码 =====

    /**
     * JWT数据的密钥
     */
    private String secretKey = "fgfdsfadsfadsafdsafdsfadsfadsfdsafdasfdsafdsafdsafds4rttrefds";

    @Override
    public String login(AdminLoginDTO adminLoginDTO) {
        // ===== 原有其它代码 =====

        // 如果程序可以执行到此处,则表示登录成功
        // 生成此用户数据的JWT
        // Claims
    	User user=(User) authenticate.getPrincipal();
        System.out.println("从认证结果中获取Principal=" + user.getClass().getName());
        Map<String,Object> claims=new HshMap<>();
        claims.put("username",user.getUsername());
        claims.put("permissions",user.getAuthorities());
        System.out.println("即将向JWT中写入数据=" + claims);
        //JWT的组成部分:Header(头),Payload(载荷),Signature(签名)
        String jwt=Jwts.builder()
            //Header:指定算法与当前数据类型
            //格式:{"alg":算法,"typ":"jwt"}
            .setHeaderParam(Header.CONTENT_TYPE,"HS256")
            .setHeaderParam(Header.TYPE,Header.JWT_TYPE)
            //Payload:通常包含Claims(自定义数据)和过期时间
            .setClaims(claims)
            .setExpiration(new Data(System.currentTimeMillis()+5*60*1000))
            //Signature:由算法和密钥(secret key)两部分组成
            .signwith(SignatureAlgorithm.HS256,secretkey)
            //打包生成
            .compact();

        // 返回JWT数据
        return jwt;
    }

}

4.更改控制器处理请求的返回值的类型(AdminController)

1.原因:

因为在控制器中,应该响应的是JSON格式的数据,所以,需要在csmall-passport中添加依赖csmall-common

并将控制器处理请求的方法的返回值类型改为JsonResult<String>,并调整返回值:

2.代码:

//http://localhost:8080/admins/login?username=root&password=123456
@RequestMapping("/login")
public JsonResult<String> login(AdminLoginDTO adminLoginDTO){
    String jwt=adminService.login(adminLoginDTO);
    return JsonResult.ok(jwt);
}

3.结果:

{
    "state":20000,
    "message":null,
    "data":"eyJjdHkiOiJIUzI1NiIsInR5cCI6IkpXVCIsImFsZyI6IkhTMjU2In0.eyJwZXJtaXNzaW9ucyI6W3siYXV0aG9yaXR5IjoiL2Ftcy9hZG1pbi9kZWxldGUifSx7ImF1dGhvcml0eSI6Ii9hbXMvYWRtaW4vcmVhZCJ9LHsiYXV0aG9yaXR5IjoiL2Ftcy9hZG1pbi91cGRhdGUifSx7ImF1dGhvcml0eSI6Ii9wbXMvcHJvZHVjdC9kZWxldGUifSx7ImF1dGhvcml0eSI6Ii9wbXMvcHJvZHVjdC9yZWFkIn0seyJhdXRob3JpdHkiOiIvcG1zL3Byb2R1Y3QvdXBkYXRlIn1dLCJleHAiOjE2NTU0MzQwMzcsInVzZXJuYW1lIjoicm9vdCJ9.8ZIfpxxjJlwNo-E3JhXwH4sZR0J5-FU-HAOMu1Tg-44"
}

注意:以上只是访问/admin/login时会执行所编写的流程(发送用户名和密码,得到含JWT的结果),并不代表真正意义的实现了“登录”!

5.登录流程

  1. 客户端提交用户名和密码到服务器
  2. 服务器认证成功后响应JWT
  3. 客户端在后续的请求中都携带JWT
  4. 服务器端验证JWT来决定是否允许访问

6.添加Knife4j(便于体现客户端携带JWT)

且在使用Knife4j时,需要在白名单中添加相关资源路径

SecurityConfiguration

@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    // ===== 原有其它代码 =====

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // ===== 原有其它代码 =====

        // URL白名单
        String[] urls = {
                "/admins/login",
                "/doc.html",  // 从本行开始,以下是新增
                "/**/*.js",
                "/**/*.css",
                "/swagger-resources",
                "/v2/api-docs",
                "/favicon.ico"
        };

        // ===== 原有其它代码 =====
    }
}

7.在控制器添加测试访问的请求配置:便于测试(AdminController)

// 以下是测试访问的请求
@GetMapping("/hello")
public String sayHello() {
    return "hello~~~";
}

由于/admins/hello不在白名单中,直接访问会出现403错误

8.使用规范

  1. JWT数据必须携带在请求头(Request Header)的Authorization属性中
  2. 服务器端在每次接收到请求后
  3. 先判断请求头是否存在Authorization
  4. Authorization的值是否有效等操作

通过过滤器实现以上检查

9.过滤器

1.创建过滤类(JwtAuthenticationFilter)

csmall-passport的根包下的security包下创建JwtAuthenticationFilter过滤器类,需要继承自OncePerRequestFilter类:

@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws servletexception, IOException {
        System.out.println("JwtAuthenticationFilter.doFilterInternal()");
    }

}

2.将自定义过滤器注册在Spring Security的相关过滤器之前

原因:

  1. 所有的过滤器都必须注册后才可以使用,且同一个项目中允许存在多个过滤器,形成过滤器链

  2. 以上用于验证JWT的过滤器应该运行在Spring Security处理登录的过滤器之前,需要在自定义的SecurityConfiguration中的configure()方法中将以上自定义的过滤器注册在Spring Security的相关过滤器之前

  3. 代码

    @Configuration
    public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
    
        // 新增
        @Autowired
        private JwtAuthenticationFilter jwtAuthenticationFilter;
        
        // ===== 原有其它代码 =====
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            // ===== 原有其它代码 =====
    
            // 注册处理JWT的过滤器
            // 此过滤器必须在Spring Security处理登录的过滤器之前
            http.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
        }
    }
    

10.完成后表现

完成后,重启项目,无论对哪个路径发出请求,在控制台都可以看出输出了过滤器中的输出语句内容,并且,在浏览器将显示一片空白。

11.JwtAuthenticationFilter

需要实现:

  • 尝试从请求头中获取JWT数据
    • 如果无JWT数据,应该直接放行,Spring Security还会进行后续的处理,例如白名单的请求将允许访问,其它请求将禁止访问
    • 如果存在JWT数据,应该尝试解析
      • 如果解析失败,应该视为错误,可以要求客户端重新登录,客户端就可以得到新的、正确的JWT,客户端在下一次提交请求时,使用新的JWT即可正确访问
      • 将解析得到的数据封装到Authentication对象中
        • Spring Security的上下文中存储的数据类型是Authentication类型
        • 为避免存入1次后,Spring Security的上下文中始终存在Authentication,在此过滤器执行的第一时间,应该清除上下文中的数据
package cn.tedu.csmall.passport.security;

import cn.tedu.csmall.common.web.JsonResult;
import cn.tedu.csmall.common.web.State;
import com.alibaba.fastjson.JSON;
import io.jsonwebtoken.*;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
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.List;

/**
 * JWT过滤器:从请求头的Authorization中获取JWT中存入的用户信息
 * 并添加到Spring Security的上下文中
 * 以致于Spring Security后续的组件(包括过滤器等)能从上下文中获取此用户的信息
 * 从而验证是否已经登录、是否具有权限等
 */
@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {

    /**
     * JWT数据的密钥
     */
    private String secretKey = "fgfdsfadsfadsafdsafdsfadsfadsfdsafdasfdsafdsafdsafds4rttrefds";

    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain filterChain) throws servletexception, IOException {
        System.out.println("JwtAuthenticationFilter.doFilterInternal()");
        // 清除Spring Security上下文中的数据
        // 避免此前曾经存入过用户信息,后续即使没有携带JWT,在Spring Security仍保存有上下文数据(包括用户信息)
        System.out.println("清除Spring Security上下文中的数据");
        SecurityContextHolder.clearContext();
        // 客户端提交请求时,必须在请求头的Authorization中添加JWT数据,这是当前服务器程序的规定,客户端必须遵守
        // 尝试获取JWT数据
        String jwt = request.getHeader("Authorization");
        System.out.println("从请求头中获取到的JWT=" + jwt);
        // 判断是否不存在jwt数据
        if (!StringUtils.hasText(jwt)) {
            // 不存在jwt数据,则放行,后续还有其它过滤器及相关组件进行其它的处理,例如未登录则要求登录等
            // 此处不宜直接阻止运行,因为“登录”、“注册”等请求本应该没有jwt数据
            System.out.println("请求头中无JWT数据,当前过滤器将放行");
            filterChain.doFilter(request, response); // 继续执行过滤器链中后续的过滤器
            return; // 必须
        }

        // 注意:此时执行时,如果请求头中携带了Authentication,日志中将输出,且不会有任何响应,因为当前过滤器尚未放行
        // 以下代码有可能抛出异常的
        // Todo 密钥和各个Key应该统一定义
        String username = null;
        String permissionsstring = null;
        try {
            System.out.println("请求头中包含JWT,准备解析此数据……");
            Claims claims = Jwts.parser().setSigningKey(secretKey).parseClaimsJws(jwt).getBody();
            username = claims.get("username").toString();
            permissionsstring = claims.get("permissions").toString();
            System.out.println("username=" + username);
            System.out.println("permissionsstring=" + permissionsstring);
        } catch (ExpiredJwtException e) {
            System.out.println("解析JWT失败,此JWT已过期:" + e.getMessage());
            JsonResult<Void> jsonResult = JsonResult.fail(
                    State.ERR_JWT_EXPIRED, "您的登录已过期,请重新登录!");
            String jsonString = JSON.toJSONString(jsonResult);
            System.out.println("响应结果:" + jsonString);
            response.setContentType("application/json; charset=utf-8");
            response.getWriter().println(jsonString);
            return;
        } catch (MalformedJwtException e) {
            System.out.println("解析JWT失败,此JWT数据错误,无法解析:" + e.getMessage());
            JsonResult<Void> jsonResult = JsonResult.fail(
                    State.ERR_JWT_MALFORMED, "获取登录信息失败,请重新登录!");
            String jsonString = JSON.toJSONString(jsonResult);
            System.out.println("响应结果:" + jsonString);
            response.setContentType("application/json; charset=utf-8");
            response.getWriter().println(jsonString);
            return;
        } catch (SignatureException e) {
            System.out.println("解析JWT失败,此JWT签名错误:" + e.getMessage());
            JsonResult<Void> jsonResult = JsonResult.fail(
                    State.ERR_JWT_SIGNATURE, "获取登录信息失败,请重新登录!");
            String jsonString = JSON.toJSONString(jsonResult);
            System.out.println("响应结果:" + jsonString);
            response.setContentType("application/json; charset=utf-8");
            response.getWriter().println(jsonString);
            return;
        } catch (Throwable e) {
            System.out.println("解析JWT失败,异常类型:" + e.getClass().getName());
            e.printstacktrace();
            JsonResult<Void> jsonResult = JsonResult.fail(
                    State.ERR_INTERNAL_SERVER_ERROR, "获取登录信息失败,请重新登录!");
            String jsonString = JSON.toJSONString(jsonResult);
            System.out.println("响应结果:" + jsonString);
            response.setContentType("application/json; charset=utf-8");
            response.getWriter().println(jsonString);
            return;
        }

        // 将此前从JWT中读取到的permissionsstring(JSON字符串)转换成Collection<? extends GrantedAuthority>
        List<SimpleGrantedAuthority> permissions
                = JSON.parseArray(permissionsstring, SimpleGrantedAuthority.class);
        System.out.println("从JWT中获取到的权限转换成Spring Security要求的类型:" + permissions);
        // 将解析得到的用户信息传递给Spring Security
        // 获取Spring Security的上下文,并将Authentication放到上下文中
        // 在Authentication中封装:用户名、null(密码)、权限列表
        // 因为接下来并不会处理认证,所以Authentication中不需要密码
        // 后续,Spring Security发现上下文中有Authentication时,就会视为已登录,甚至可以获取相关信息
        Authentication authentication
                = new UsernamePasswordAuthenticationToken(username, null, permissions);
        SecurityContextHolder.getContext().setAuthentication(authentication);
        System.out.println("将解析得到的用户信息传递给Spring Security");
        // 放行
        System.out.println("JwtAuthenticationFilter 放行");
        filterChain.doFilter(request, response);
    }

}

java – 自定义authenticationFilter Spring Security 3.2

java – 自定义authenticationFilter Spring Security 3.2

对于一个项目,我尝试使用Spring Security 3.2作为基本安全性.因为这个项目已经启动并运行,所以我已经拥有了另一个(自己的)安全层.因此,我制作了一个自定义身份验证提供程序来融化安全层.工作正常,直到我还需要进行自定义匿名身份验证(Spring Security Documentation,chapter 13).

所以我做了一个自定义过滤器并删除了orignal过滤器:

豆子:

和te Java类:

public class SecurityAnonymousAuthenticationFilter extends GenericFilterBean implements InitializingBean {
    public void doFilter(ServletRequest req,ServletResponse res,FilterChain chain) throws IOException,ServletException {
        logger.info("Entering doFilter method");
        //implementation code here
    }

    //other methods
}

问题是请求服务器时不调用doFilter方法.但是调用了init方法afterPropertiesSet()…是否有人理解为什么我的customFilter没有被触发?

附:我确实在web.xml文件中命名了delegatingFilterProxy,所以这不是问题.

最佳答案
由于ANONYMOUS_FILTER是与名称空间相关的过滤器.您必须避免引用特定过滤器psoition的任何名称空间标记:

   

有关进一步参考,请参阅2.3.5:http://static.springsource.org/spring-security/site/docs/3.0.x/reference/ns-config.html中的Spring安全性文档

编辑:并确保保留< anonymous-enabled = false />标签.

编辑2:纠正了我的回答.这种配置应该有效.如果没有,那么我们需要开始查看更大的图片并且您必须发布更多应用,从完整配置开始.

java – 配置Spring Security以使用customPasswordAuthenticationFilter

java – 配置Spring Security以使用customPasswordAuthenticationFilter

我已经实现了自己的LowerCaseUsernamePasswordAuthenticationFilter,它只是UsernamePasswordAuthenticationFilter的一个子类.

但是现在我的问题是如何配置Spring的安全性来使用这个过滤器.

到目前为止,我使用:

<security:http auto-config="true" use-expressions="true">
    <security:form-login login-processing-url="/resources/j_spring_security_check" login-page="/login" authentication-failure-url="/login?login_error=t" />
    <security:logout logout-url="/resources/j_spring_security_logout" />

    <security:intercept-url pattern="/**" access="isAuthenticated()" requires-channel="${cfma.security.channel}" />
</security:http>

我真的要打开自动配置,需要手动配置所有的过滤器吗? – 如果这是真的,有人可以提供一个例子吗?

添加简单安全性的方法:自定义过滤器:

<security:http ...>

   <security:form-login login-processing-url="/resources/j_spring_security_check" login-page="/login" authentication-failure-url="/login?login_error=t" />
   <security:custom-filter ref="lowerCaseUsernamePasswordAuthenticationFilter" position="FORM_LOGIN_FILTER"/>
   ...
 </security:http>

确实导致该消息的异常:

Configuration problem: Filter beans <lowerCaseUsernamePasswordAuthenticationFilter> and ‘Root bean: class [org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factorybeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null’ have the same ‘order’ value. When using custom filters,please make sure the positions do not conflict with default filters. Alternatively you can disable the default filters by removing the corresponding child elements from and avoiding the use of .

解决方法

我已经通过手工编写所需的自动配置的bean来完成.这是结果:
<!-- HTTP security configurations -->
<security:http auto-config="false" use-expressions="true" entry-point-ref="loginUrlAuthenticationEntryPoint">

    <!--
    <security:form-login login-processing-url="/resources/j_spring_security_check" login-page="/login" authentication-failure-url="/login?login_error=t" />
        replaced by lowerCaseUsernamePasswordAuthenticationFilter
        the custom-filter with position FORM_LOGIN_FILTER requries that auto-config is false!
     -->
    <security:custom-filter ref="lowerCaseUsernamePasswordAuthenticationFilter" position="FORM_LOGIN_FILTER"/>
    <security:logout logout-url="/resources/j_spring_security_logout" />

    <security:intercept-url pattern="/**" access="isAuthenticated()" />
</security:http>

<bean id="loginUrlAuthenticationEntryPoint"https://www.jb51.cc/tag/ecurity/" target="_blank">ecurity.web.authentication.LoginUrlAuthenticationEntryPoint">
    <property name="loginFormUrl" value="/login"/>
</bean>

<bean id="lowerCaseUsernamePasswordAuthenticationFilter"https://www.jb51.cc/tag/ecurity/" target="_blank">ecurity.LowerCaseUsernamePasswordAuthenticationFilter">
    <property name="filterProcessesUrl" value="/resources/j_spring_security_check"/>
    <property name="authenticationManager" ref="authenticationManager"/>
    <property name="authenticationFailureHandler">
        <beanhttps://www.jb51.cc/tag/ecurity/" target="_blank">ecurity.web.authentication.SimpleUrlAuthenticationFailureHandler">
            <property name="defaultFailureUrl" value="/login?login_error=t"/>       
        </bean>
    </property>
</bean>

JwtUser JwtAuthenticationEntryPoint JwtAuthorizationTokenFilter JwtUserDetailsService

JwtUser JwtAuthenticationEntryPoint JwtAuthorizationTokenFilter JwtUserDetailsService

package me.zhengjie.core.security;

import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.AllArgsConstructor;
import lombok.Getter;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import java.sql.Timestamp;
import java.util.*;

/**
 * @author jie
 * @date 2018-11-23
 */
@Getter
@AllArgsConstructor
public class JwtUser implements UserDetails {

    @JsonIgnore
    private final Long id;

    private final String username;

    @JsonIgnore
    private final String password;

    private final String avatar;

    private final String email;

    @JsonIgnore
    private final Collection<? extends GrantedAuthority> authorities;

    private final boolean enabled;

    private Timestamp createTime;

    @JsonIgnore
    private final Date lastPasswordResetDate;

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

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

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

    @JsonIgnore
    @Override
    public String getpassword() {
        return password;
    }

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

    /**
     * 在我们保存权限的时候加上了前缀ROLE_,因此在这里需要处理下数据
     * @return
     */
    public Collection getRoles() {
        Set<String> roles = new LinkedHashSet<>();
        for (GrantedAuthority authority : authorities) {
            roles.add(authority.getAuthority().substring(5));
        }
        return roles;
    }
}

 

package me.zhengjie.core.security;

import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;

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

@Component
public class
JwtAuthenticationEntryPoint implements AuthenticationEntryPoint,Serializable {

    private static final long serialVersionUID = -8970718410437077606L;

    @Override
    public void commence(HttpServletRequest request,HttpServletResponse response,AuthenticationException authException) throws IOException {
        /**
         * 当用户尝试访问安全的REST资源而不提供任何凭据时,将调用此方法发送401 响应
         */
        response.sendError(HttpServletResponse.SC_UNAUTHORIZED,authException==null?"Unauthorized":authException.getMessage());
    }
}
package me.zhengjie.core.security;

import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;

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

@Component
public class
JwtAuthenticationEntryPoint implements AuthenticationEntryPoint,authException==null?"Unauthorized":authException.getMessage());
    }
}
package me.zhengjie.core.security;

import io.jsonwebtoken.ExpiredJwtException;
import me.zhengjie.core.utils.JwtTokenUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
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;

@Component
public class JwtAuthorizationTokenFilter extends OncePerRequestFilter {

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    private final UserDetailsService userDetailsService;
    private final JwtTokenUtil jwtTokenUtil;
    private final String tokenHeader;

    public JwtAuthorizationTokenFilter(@Qualifier("jwtUserDetailsService") UserDetailsService userDetailsService,JwtTokenUtil jwtTokenUtil,@Value("${jwt.header}") String tokenHeader) {
        this.userDetailsService = userDetailsService;
        this.jwtTokenUtil = jwtTokenUtil;
        this.tokenHeader = tokenHeader;
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request,FilterChain chain) throws servletexception,IOException {
        logger.debug("processing authentication for ‘{}‘",request.getRequestURL());

        final String requestHeader = request.getHeader(this.tokenHeader);

        String username = null;
        String authToken = null;
        if (requestHeader != null && requestHeader.startsWith("Bearer ")) {
            authToken = requestHeader.substring(7);
            try {
                username = jwtTokenUtil.getUsernameFromToken(authToken);
            } catch (ExpiredJwtException e) {
               logger.error(e.getMessage());
            }
        }

        logger.debug("checking authentication for user ‘{}‘",username);

        if (username != null && SecurityContextHolder.getContext().getAuthentication() == null) {
            logger.debug("security context was null,so authorizating user");

            // It is not compelling necessary to load the use details from the database. You Could also store the information
            // in the token and read it from it. It‘s up to you ;)
            UserDetails userDetails = this.userDetailsService.loadUserByUsername(username);

            // For simple validation it is completely sufficient to just check the token integrity. You don‘t have to call
            // the database compellingly. Again it‘s up to you ;)
            if (jwtTokenUtil.validatetoken(authToken,userDetails)) {
                UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(userDetails,null,userDetails.getAuthorities());
                authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                logger.info("authorizated user ‘{}‘,setting security context",username);
                SecurityContextHolder.getContext().setAuthentication(authentication);
            }
        }
        chain.doFilter(request,response);
    }
}
package me.zhengjie.core.service;

import me.zhengjie.common.exception.EntityNotFoundException;
import me.zhengjie.common.utils.ValidationUtil;
import me.zhengjie.core.security.JwtUser;
import me.zhengjie.system.domain.Permission;
import me.zhengjie.system.domain.Role;
import me.zhengjie.system.domain.User;
import me.zhengjie.system.repository.PermissionRepository;
import me.zhengjie.system.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author jie
 * @date 2018-11-22
 */
@Service
@Transactional(propagation = Propagation.SUPPORTS,readOnly = true,rollbackFor = Exception.class)
public class JwtUserDetailsService implements UserDetailsService {

    @Autowired
    private UserRepository userRepository;

    @Autowired
    private PermissionRepository permissionRepository;

    @Override
    public UserDetails loadUserByUsername(String username){

        User user = null;
        if(ValidationUtil.isEmail(username)){
            user = userRepository.findByEmail(username);
        } else {
            user = userRepository.findByUsername(username);
        }

        if (user == null) {
            throw new EntityNotFoundException(User.class,"name",username);
        } else {
            return create(user);
        }
    }

    public UserDetails create(User user) {
        return new JwtUser(
                user.getId(),user.getUsername(),user.getpassword(),user.getAvatar(),user.getEmail(),mapToGrantedAuthorities(user.getRoles(),permissionRepository),user.getEnabled(),user.getCreateTime(),user.getLastPasswordResetTime()
        );
    }

    private static List<GrantedAuthority> mapToGrantedAuthorities(Set<Role> roles,PermissionRepository permissionRepository) {

        Set<Permission> permissions = new HashSet<>();
        for (Role role : roles) {
            Set<Role> roleSet = new HashSet<>();
            roleSet.add(role);
            permissions.addAll(permissionRepository.findByRoles(roleSet));
        }

        return permissions.stream()
                .map(permission -> new SimpleGrantedAuthority("ROLE_"+permission.getName()))
                .collect(Collectors.toList());
    }
}
package me.zhengjie.core.service;

import me.zhengjie.common.exception.EntityNotFoundException;
import me.zhengjie.common.utils.ValidationUtil;
import me.zhengjie.core.security.JwtUser;
import me.zhengjie.system.domain.Permission;
import me.zhengjie.system.domain.Role;
import me.zhengjie.system.domain.User;
import me.zhengjie.system.repository.PermissionRepository;
import me.zhengjie.system.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author jie
 * @date 2018-11-22
 */
@Service
@Transactional(propagation = Propagation.SUPPORTS,PermissionRepository permissionRepository) {

        Set<Permission> permissions = new HashSet<>();
        for (Role role : roles) {
            Set<Role> roleSet = new HashSet<>();
            roleSet.add(role);
            permissions.addAll(permissionRepository.findByRoles(roleSet));
        }

        return permissions.stream()
                .map(permission -> new SimpleGrantedAuthority("ROLE_"+permission.getName()))
                .collect(Collectors.toList());
    }
}

关于在Spring Security UsernamePasswordAuthenticationFilter JWT身份验证中设置自定义登录URL的介绍现已完结,谢谢您的耐心阅读,如果想了解更多关于day76(Spring Security + JWT,JwtAuthenticationFilter)、java – 自定义authenticationFilter Spring Security 3.2、java – 配置Spring Security以使用customPasswordAuthenticationFilter、JwtUser JwtAuthenticationEntryPoint JwtAuthorizationTokenFilter JwtUserDetailsService的相关知识,请在本站寻找。

本文标签: