暂无图片
暂无图片
6
暂无图片
暂无图片
暂无图片

Java安全框架(四):Spring Security + JWT综合应用(流程梳理)

一叶扁舟 2020-08-27
3292

Java安全框架(一):Spring Security基础
Java安全框架(二):Spring Security工作原理
Java安全框架(三):JWT(Json Web Token)

一、依赖

<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>

二、SecurityConfig配置

1、登录认证授权配置

(1)指定哪些网页需要登录,哪些不需要(直接配置
(2)配置登录页面拦截器,以便自定义登录页面(继承重写
(3)配置请求过滤,默认是session验证,需要重新配置token验证(继承重写

2、加密配置

(1)实现UserDetailService重写loadUserByUsername方法,用于将数据库中用户角色权限等信息放入安全上下文。(实现重写
(2)重写加密算法,使用md5进行加密(继承重写

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private UserDetailsService myCustomUserService;
    @Autowired
    private MyPasswordEncoder myPasswordEncoder;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                //关闭跨站请求防护
                .cors().and().csrf().disable()
                //允许不登陆就可以访问的方法,多个用逗号分隔
                .authorizeRequests().antMatchers("/test").permitAll()
                //其他的需要授权后访问
                .anyRequest().authenticated()

                .and()
                //增加登录拦截
                .addFilter(new JWTLoginFilter(authenticationManager()))
                //增加请求过滤
                .addFilter(new JWTAuthenticationFilter(authenticationManager()))
                // 前后端分离是无状态的,所以暫時不用session,將登陆信息保存在token中。
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
    }
    @Override
    public void configure(AuthenticationManagerBuilder auth) throws Exception {
        //覆盖UserDetailsService类
        auth.userDetailsService(myCustomUserService)
                //覆盖默认的密码验证类
                .passwordEncoder(myPasswordEncoder);
    }
}

三、实现配置文件中的自定义类

1、配置登录拦截器

作用:Security有自己的登录页面,配置拦截可以实现自定义登录
做法:自定义JWTLoginFilter,继承UsernamePasswordAuthenticationFilter 接口,拦截登录并做自定义登录(将token等以json格式返回,方便做前后端分离和单点登录等)。

public class JWTLoginFilter extends UsernamePasswordAuthenticationFilter {

}

2、配置请求过滤

作用:对controller中需要登录才能访问的方法进行拦截(token验证),返回json提示信息。
做法:JWTAuthenticationFilter 类继承BasicAuthenticationFilter 接口,重写请求过滤

public class JWTAuthenticationFilter extends BasicAuthenticationFilter {

}

3、配置读取数据库信息放入安全上下文

作用:可以根据token中传来的信息,从数据库查询用户角色权限,放入安全上传下文,便于后续认证授权
做法:实现UserDetailsService,重写loadUserByUsername方法

@Component
public class MyCustomUserService implements UserDetailsService {

    /**
     * 登陆验证时,通过username获取用户的所有权限信息
     * 并返回UserDetails放到spring的全局缓存SecurityContextHolder中,以供授权器使用
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //在这里可以自己调用数据库,对username进行查询,看看在数据库中是否存在
        return myUserDetail;
    }
}

备注:此处从数据库查的用户权限和角色,放入了安全上下文,因此,就可以直接在controller需要权限认证的方法上加权限限制的注解了。

4、重写加密算法

作用:默认是不加密的,用户密码不加密存入数据库是很可怕的,因此需要配置密码加密,使用md5加密(不可逆的)
做法:实现PasswordEncoder实现加密和密码验证的方法,并编写md5加密算法

  • 自定义加密
public class EmcsPasswordEncoder implements PasswordEncoder {
    @Override
    public boolean matches(CharSequence rawPassword, String encodedPassword) {
        return encodedPassword.equals(Md5Encoder.encode((String)rawPassword));
    }
    @Override
    public String encode(CharSequence rawPassword) {
        return Md5Encoder.encode((String)rawPassword);
    }
}
  • 编写md5加密工具类
public class Md5Encoder {
    private static final String SALT = "yang";
    public static String encode(String password) {
        password = password + SALT;
        MessageDigest md5 = null;
        try {
            md5 = MessageDigest.getInstance("MD5");
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        char[] charArray = password.toCharArray();
        byte[] byteArray = new byte[charArray.length];
        for (int i = 0; i < charArray.length; i++)
            byteArray[i] = (byte) charArray[i];
        byte[] md5Bytes = md5.digest(byteArray);
        StringBuffer hexValue = new StringBuffer();
        for (int i = 0; i < md5Bytes.length; i++) {
            int val = ((int) md5Bytes[i]) & 0xff;
            if (val < 16) {
                hexValue.append("0");
            }
            hexValue.append(Integer.toHexString(val));
        }
        return hexValue.toString();
    }
}

以上配置完成就实现了前后端分离、md5加密、token验证、权限控制。

四、用户鉴权工具类

作用:在项目中,搭建好以上框架后,根据上下文可以获取到用户角色和权限,为了方便鉴权则可以编写一个配置类,需要鉴权的时候直接调用该工具类进行鉴权。

public class UserUtils {

    public static final String SYS = "sys";

    public static Boolean isSys() {
        if (!UserUtils.isLogin()) {
            return false;
        }
        return StringUtils.contains(getRole(), SYS);
    }

    public static String getRole() {
        GrantedAuthority[] authorities = getAuth().getAuthorities().toArray(new GrantedAuthority[1]);
        if (authorities.length > 0) {
            for (GrantedAuthority authority : authorities) {
                String authorityName = authority.getAuthority();
                if (authorityName.startsWith(RoleConfig.ROLE_PREFIX)) {
                    return authorityName.substring(5);
                }
            }
        }
        return null;
    }
}

五、单点登录配置

实质:token验证

1、添加依赖

<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>

2、增加Security配置文件

由于单点登录是通过token验证的,所以,就没有了登录拦截和密码校验步骤,只需要请求过滤即可

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                //关闭跨站请求防护
                .cors().and().csrf().disable()
                //允许不登陆就可以访问的方法,多个用逗号分隔
                .authorizeRequests()
                //.antMatchers("/index").permitAll()
                //其他的需要授权后访问
                .anyRequest().authenticated()
                .and()
                //增加请求过滤
                .addFilterBefore(new JWTAuthenticationFilter(),
                        UsernamePasswordAuthenticationFilter.class)
                // 前后端分离是无状态的,所以暫時不用session,將登陆信息保存在token中。
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
    }
}

3、token验证

需要根据JWT的算法解析token

public class JWTAuthenticationFilter extends BasicAuthenticationFilter {

}

以上就是单点登录实现的流程。

「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论