SpringSecurity实战细节记录

一、关于密码加密

   一般情况下选择Base64或者MD5加密,其中SpringSecurity中有BCryptPasswordEncoder类供密码加密,三者加密选择优先级如下:

   BCryptPasswordEncoder>MD5>Base64

注:spring security中的BCryptPasswordEncoder方法采用SHA-256 +随机盐+密钥对密码进行加密。SHA系列是Hash算法,不是加密算法,使用加密算法意味着可以解密(这个与编码/解码一样),但是采用Hash处理,其过程是不可逆的。

每次加密后的密码都不一样,每次的随机盐都保存在加密后的密码中。在比较的时候,随机盐重新被取出。即加密后的密码中,前部分已经包含了盐信息。

二、关于JWT工具中密钥(盐)签名

    1、当使用对称加密时考虑使用Jwts工具类,用户信息等可以在负载中通过链式调用直接加入进去。

a、加密

/**
 * JWT工具类
 * 注意点:
 * 1.生成的token是可以通过base64进行解密出明文信息
 * 2.base64进行解密出的明文信息,修改再进行编码,则会解密失败
 * 3.无法作废已经颁布的令牌token,除非改密钥
 */
public class JWTUtils {
    /**
     * 过期时间为一周
     */
    private static final long Expire=60000*60*24*7;
    /**
     * 密钥
     */
    private static final String secret="xdclass.net168";
    /**
     * 令牌前缀
     */
    private static final String Token_PreFix="xdclass";
    /**
     * subject主题
     */
    private static final String SUBJECT="xdclass";

    /**
     * 根据用户信息生成令牌
     * @param user
     * @return
     */
    public static String geneJsonWebToken(User user){
        String token = Jwts.builder().setSubject(SUBJECT)
                .claim("head_img", user.getHeadImg())
                .claim("id", user.getId())
                .claim("name", user.getName())
                .setIssuedAt(new Date())
                .setExpiration(new Date(System.currentTimeMillis() + Expire))
                .signWith(SignatureAlgorithm.HS256, secret).compact();
        token=Token_PreFix+token;
        return token;
    }

b、解密

public static Claims checkJWT(String token){
        try{
            final Claims claims = Jwts.parser().setSigningKey(secret)
                    .parseClaimsJws(token.replace(Token_PreFix, ""))
                    .getBody();
            return claims;
        }catch (Exception e){
            return null;
        }
    }

    2、当使用非对称加密时考虑使用JwtHelper工具类,其中用户等信息需单独通过数据结构存储后放入参数中

a、加密

Map<String,String> map=new HashMap<>();
        map.put("userName", user.getUsername());
        map.put("password", user.getPassword());
        Jwt encode = JwtHelper.encode(JSON.toJSONString(map), new RsaSigner(rsaPrivateKey));
        String encoded = encode.getEncoded();


b、解密

        Jwt jwt = JwtHelper.decodeAndVerify(token, new RsaVerifier(publicKey));
        String claims = jwt.getClaims();
        System.out.println("claims"+claims);
        return claims;

三、关于如何生成公私钥

     公私钥生成主要有两种方式,一种是利用jdk自带的keytool工具生成,另外一种是通过代码生成,这里讲解代码生成方式

package com.dispart.dispartuser.Utils;

import org.apache.commons.codec.binary.Base64;
import sun.security.mscapi.CKeyPairGenerator;
import sun.security.pkcs.PKCS8Key;
import sun.security.util.DerValue;

import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.util.HashMap;
import java.util.Map;

public class GenerateKey {
//    加密算法
    private static final String KEY_ALGORITHM = "RSA";
//    公钥key
    private static final String PUB_KEY="publicKey";
//    私钥key
    private static final String PRI_KEY="privateKey";

    public static Map<String,String> generateKey() throws NoSuchAlgorithmException {
        Map<String,String> keyMap=new HashMap<>();
        KeyPairGenerator instance = KeyPairGenerator.getInstance(KEY_ALGORITHM);
        KeyPair keyPair = instance.generateKeyPair();
        PrivateKey privateKey = keyPair.getPrivate();
        PublicKey publicKey = keyPair.getPublic();
        //Base64 编码
        byte[] privateKeyEncoded = privateKey.getEncoded();
        String privateKeyStr = Base64.encodeBase64String(privateKeyEncoded);
        byte[] publicKeyEncoded = publicKey.getEncoded();
        String publicKeyStr=Base64.encodeBase64String(publicKeyEncoded);
        keyMap.put(PUB_KEY,publicKeyStr);
        keyMap.put(PRI_KEY,privateKeyStr);
        return keyMap;
    }

    public static void main(String[] args) throws NoSuchAlgorithmException {
        generateKey();
    }

}

KeyPairGenerator工具类是springsecurity包中嵌套的类

如何将String类转换为RSAPublicKey以及RSAPrivateKey?

 /**
     * 得到公钥
     *
     * @param publicKey
     *            密钥字符串(经过base64编码)
     * @throws Exception
     */
    public static RSAPublicKey getPublicKey(String publicKey) throws NoSuchAlgorithmException, InvalidKeySpecException {
        // 通过X509编码的Key指令获得公钥对象
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(Base64.decodeBase64(publicKey));
        RSAPublicKey key = (RSAPublicKey) keyFactory.generatePublic(x509KeySpec);
        return key;
    }

    /**
     * 得到私钥pkcs8
     *
     * @param privateKey
     *            密钥字符串(经过base64编码)
     * @throws Exception
     */
    public static RSAPrivateKey getPrivateKey(String privateKey)
            throws NoSuchAlgorithmException, InvalidKeySpecException {
        // 通过PKCS#8编码的Key指令获得私钥对象
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(Base64.decodeBase64(privateKey));
        RSAPrivateKey key = (RSAPrivateKey) keyFactory.generatePrivate(pkcs8KeySpec);
        return key;
    }

四、SpringSecurity配置类配置

package com.example.springsecurityjwt.Config;

import org.springframework.beans.factory.annotation.Autowired;
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.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true,securedEnabled = true,jsr250Enabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    AuthenticationSuccess authenticationSuccess;
    @Autowired
    AuthenticationFail authenticationFail;
    @Autowired
    LogoutSuccess logoutSuccess;
    @Autowired
    myUserDeatil myUserDeatil;
    @Autowired
    private PasswordEncoder passwordEncoder;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests().antMatchers("/index","/register","/user/login","/user/fail").permitAll()//登录注册无需认证
                .anyRequest().authenticated();//其余请求皆需认证
        http.formLogin().loginPage("/index").loginProcessingUrl("/user/login").successForwardUrl("/user/login").failureUrl("/user/fail")
//                .successHandler(authenticationSuccess).failureHandler(authenticationFail)
                .and().logout().logoutSuccessHandler(logoutSuccess);
        http.userDetailsService(myUserDeatil).csrf().disable();

    }


//    @Override
//    protected void configure(HttpSecurity http) throws Exception {
//        http.formLogin().loginPage("/login.html").loginProcessingUrl("/user/login").successForwardUrl("/user/index").permitAll()
//                .and().authorizeRequests().anyRequest().authenticated()
//                .and().csrf().disable();
//    }

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

    public static void main(String[] args) {
        String encode = new BCryptPasswordEncoder().encode("678");
        System.out.printf( encode);
        boolean bo = new BCryptPasswordEncoder().matches("678", "$2a$10$nm5LcfRw0y2c18FbpMulFef9o4tPZGSmKsZ1WvxMOSw34lEKILDIK");
        System.out.println(bo);
    }
}

细节:关于配置类中主要有两个大的模块配置

1.protected void configure(HttpSecurity http) throws Exception

重写方法中有几个点需重点注意,一是授权请求,二是登录页配置以及拦截请求处理方法。

http.userDetailsService(myUserDeatil)将用户信息注册进去,一定要配置

2.protected void configure(AuthenticationManagerBuilder auth) throws Exception

该方法将从数据库返回user对象指定加密方式和前端传回来的值进行匹配

五、user模块一定要实现public class User implements UserDetails接口

如果username和password和自定义的名称不一样,需重写get方法。

六、关于过滤器链的注意事项

.successHandler(authenticationSuccess).failureHandler(authenticationFail)

处理逻辑是在SpringSecurity自身的处理链之前,如果需要调用一定要完善其逻辑。

七、登录界面演示

输入正确的用户名以及密码

点击登录后,路由到成功/user/login界面

输入错误的用户名以及密码,路由到/user/fail失败界面

, 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值