密码加密和微服务鉴权JWT

简介: Spring Security是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架。它提供了一组可以在Spring应用上下文中配置的Bean,充分利用了Spring IoC,DI(控制反转Inversion of Control ,DI:Dependency Injection 依赖注入)和AOP(面向切面编程)功能,为应用系统提供声明式的安全访问控制功能,减少了为企业系统安全控制编写大量重复代码的工作。

1.BCrypt密码加密

(1)工程中引入jar
<dependency> 
<groupId>org.springframework.boot</groupId> 
<artifactId>spring‐boot‐starter‐security</artifactId> 
</dependency>
(2)添加配置类

我们在添加了spring security依赖后,所有的地址都被spring security所控制了,我们目
前只是需要用到BCrypt密码加密的部分,所以我们要添加一个配置类,配置为所有地址
都可以匿名访问。

/*** 安全配置类 */
@Configuration 
@EnableWebSecurity 
public class WebSecurityConfig extends WebSecurityConfigurerAdapter{
    @Override 
    protected void configure(HttpSecurity http) throws Exception { 
        http 
            .authorizeRequests() //表示开始需要的权限认证
            .antMatchers("/**").permitAll() //拦截路径和需要的权限			                           .anyRequest().authenticated()  //任何请求和授权                               
            .and().csrf().disable();//固定写法,拦截csrf
    }
}

修改工程的Application, 配置bean

@Bean 
public BCryptPasswordEncoder bcryptPasswordEncoder(){ 
    return new BCryptPasswordEncoder();
}
(3)密码加密
@Autowired
BCryptPasswordEncoder encoder; 
public void add(Admin admin) { 
    //主键值
    admin.setId(idWorker.nextId()+""); 
    //密码加密
    String newpassword = encoder.encode(admin.getPassword());
    //加密后的密码 
    admin.setPassword(newpassword); 
    //保存到数据库
    adminDao.save(admin);
}
(4)登录时密码校验
/*** 根据登陆名和密码查询 
* @param loginname
* @param password 
* @return */ 
public Admin findByLoginnameAndPassword(String loginname, String password){
    Admin admin = adminDao.findByLoginname(loginname); 
    if( admin!=null && encoder.matches(password,admin.getPassword())) 
    {
        return admin; //登陆成功
    }else{
        return null;//登陆失败
    } 
}

数据库中的密码为以下形式

$2a$10$a/EYRjdKwQ6zjr0/HJ6RR.rcA1dwv1ys7Uso1xShUaBWlIWTyJl5S

2.基于JWTToken认证机制实现

​ JSON Web Token(JWT)是一个非常轻巧的规范。这个规范允许我们使用JWT在用
户和服务器之间传递安全可靠的信息。

(1)JWT组成

​ 一个JWT实际上就是一个字符串,它由三部分组成,头部、载荷与签名。
​ jwt的第三部分是一个签证信息,这个签证信息由三部分组成:

header(base64后)
payload(base64后)
secret(加盐)

​ 这个部分需要base64加密后的header和base64加密后的payload使用.连接组成的字符
串,然后通过header中声明的加密方式进行加盐secret组合加密,然后就构成了jwt的第
三部分。

(2) JJWT快速入门
<dependency> 
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId> 
    <version>0.6.0</version>
</dependency>

创建类CreateJwtTest,用于生成token

public class CreateJwtTest {
    public static void main(String[] args) {
        //为了方便测试,我们将过期时间设置为1分钟 
        long now = System.currentTimeMillis();
        //当前时间 
        long exp = now + 1000*60;        //过期时间为1分钟 
        JwtBuilder builder= Jwts.builder().setId("888") //载荷  jwts类名
            .setSubject("小白") //个人信息
            .setIssuedAt(new Date()) //设置登陆时间
            .signWith(SignatureAlgorithm.HS256,"itcast")//加盐 
             .setExpiration(new Date(exp))//设置过期时间
            .claim("roles","admin") //自定义属性
            .claim("logo","logo.png");
        System.out.println( builder.compact() );
    } 
}

token解析

public class ParseJwtTest {
    public static void main(String[] args) {
        String token="eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI4ODgiLCJzdWIiOiLlsI_nmb0iLCJpYXQiO jE1MjM0MTM0NTh9.gq0J‐cOM_qCNqU_s‐d_IrRytaNenesPmqAIhQpYXHZk"; 
        Claims claims = Jwts.parser().setSigningKey("itcast")
            .parseClaimsJws(token).getBody();
        System.out.println("id:"+claims.getId()); 
        System.out.println("subject:"+claims.getSubject()); 
        System.out.println("IssuedAt:"+claims.getIssuedAt()); 
        System.out.println("roles:"+claims.get("roles")); 
        System.out.println("logo:"+claims.get("logo"));
        SimpleDateFormat sdf=new SimpleDateFormat("yyyy‐MM‐dd hh:mm:ss"); 
        System.out.println("签发时间:"+sdf.format(claims.getIssuedAt()));
        System.out.println("过期时 间:"+sdf.format(claims.getExpiration())); 
        System.out.println("当前时间:"+sdf.format(new Date()) );
    }
}

综上所述,自己可以编写一个工具类。

(3)用户登录分发tocken
@Autowired private JwtUtil jwtUtil;
/**
* 用户登陆
* @param loginname 
* @param password 
* @return */ 
@RequestMapping(value="/login",method=RequestMethod.POST) 
public Result login(@RequestBody Map<String,String> loginMap){
    Admin admin = adminService.findByLoginnameAndPassword(loginMap.get("loginname"), loginMap.get("password")); 
    if(admin!=null){ 
        //生成token 
        String token = jwtUtil.createJWT(admin.getId(), admin.getLoginname(), "admin"); Map map=new HashMap();
        map.put("token",token);
        map.put("name",admin.getLoginname());
        //登陆名
        return new Result(true,StatusCode.OK,"登陆成功",map);
    }
    else{
        return new Result(false,StatusCode.LOGINERROR,"用户名或密码错 误");
    } 
}

JwtUtil是token的生成和解析的工具类。当用户登陆的时候为每一个用户提供一个tocken有效时间是一个小时,

用于权限访问。

(4)使用拦截器方式实现token鉴权

创建拦截器JWtFilter

@Component
public class JwtFilter extends HandlerInterceptorAdapter { 
    @Autowired private JwtUtil jwtUtil;
    @Override 
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("经过了拦截器"); 
        final String authHeader = request.getHeader("Authorization");
        if (authHeader != null && authHeader.startsWith("Bearer ")) {
            final String token = authHeader.substring(7); // The part after "Bearer " 
            Claims claims = jwtUtil.parseJWT(token);
            if (claims != null) { 
                if("admin".equals(claims.get("roles"))){
                //如果是管理员
                request.setAttribute("admin_claims", claims); 
            }if("user".equals(claims.get("roles"))){
                //如果是用户 
                request.setAttribute("user_claims", claims); 
            }
                                } 
        }
        return true; 
    } 
}

配置拦截器

@Configuration 
public class ApplicationConfig extends WebMvcConfigurationSupport { 
    @Autowired 
    private JwtFilter jwtFilter; 
    @Override
    public void addInterceptors(InterceptorRegistry registry) {        
        registry.addInterceptor(jwtFilter).
        addPathPatterns("/**").
        excludePathPatterns("/**/login");
                                                              }
}

我们通过session就可以判断用户的权限,同时也可以防止伪造请求。

# 框架 

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×