微服务项目 -- day06 密码加密与微服务鉴权JWT

一、BCrypt加密


1、使用BCrypt加密准备工作


(1)、scf_user项目中加入BCrypt依赖


        
        
            org.springframework.boot
            spring-boot-starter-security
        


(2)、scf_user添加配置类


        scf_user项目中加入springsecurity依赖之后,所有的请求都会被springsecurity控制,我们只需要加密功能,因此,需要添加配置类,配置所有的地址都可以匿名访问。


package com.scf.user;

import org.springframework.context.annotation.Configuration;
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;

/**
 *  安全配置类
 */
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter{
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .authorizeRequests()
                .antMatchers("/**").permitAll() //配置所有的请求都可以匿名访问
                .anyRequest().authenticated()
                .and().csrf().disable();
    }
}


(3)、修改scf_user的启动引导类,配置bean


微服务项目 -- day06 密码加密与微服务鉴权JWT_第1张图片


2、管理员密码加密与校验


AdminDao.java


    /**
     * 根据登录名查询管理员
     * @param loginname
     * @return
     */
    public Admin findByLoginname(String loginname);


AdminService.java


    @Autowired
    private BCryptPasswordEncoder encoder;

    /**
     * 增加管理员
     * 添加时对密码进行加密
     *
     * @param admin
     */
    public void add(Admin admin) {
        admin.setId(idWorker.nextId() + "");
        //对密码进行加密
        String password = encoder.encode(admin.getPassword());
        admin.setPassword(password);
        adminDao.save(admin);
    }

    /**
     * 根据用户名和密码查询管理员
     * @param loginname
     * @param password
     * @return
     */
    public Admin findAdminByLoginnameAndPassword(String loginname, String password){
        Admin admin = adminDao.findByLoginname(loginname);
        if(admin != null && encoder.matches(password, admin.getPassword())){
            return admin;
        }else{
            return null;
        }
    }


AdminController.java


	@RequestMapping(value = "/login", method = RequestMethod.POST)
	public Result login(@RequestBody Map loginMap){
		Admin admin = adminService.findAdminByLoginnameAndPassword(loginMap.get("loginname"), loginMap.get("password"));
		if(admin != null){
			return new Result(true, StatusCode.OK, "登录成功");
		}else{
			return new Result(false, StatusCode.LOGINERROR, "用户名或者密码错误");
		}
	}


3、用户密码加密


UserDao.java


    /**
     * 根据手机号查询用户
     * @param mobile
     * @return
     */
    public User findByMobile(String mobile);


UserService.java


    @Autowired
    private BCryptPasswordEncoder encoder;

    /**
     * 注册用户
     * @param user
     * @param code
     */
    public void register(User user, String code) {
        //判断验证码是否正确
        String redisCode = (String) redisTemplate.opsForValue().get("smscode_" + user.getMobile());
        if (redisCode == null) {
            throw new RuntimeException("请点击获取正确的验证码!");
        }
        if (!redisCode.equalsIgnoreCase(code)) {
            throw new RuntimeException("请输入正确的验证码!");
        }
        //给密码加密
        String password = encoder.encode(user.getPassword());
        user.setPassword(password);

        //设置用户属性字段
        user.setId(idWorker.nextId() + "");
        user.setFollowcount(0);//关注数
        user.setFanscount(0);//粉丝数
        user.setOnline(0L);//在线时长
        user.setRegdate(new Date());//注册时间
        user.setUpdatedate(new Date());//修改时间
        user.setLastdate(new Date());//最后登录时间
        userDao.save(user);
    }

    /**
     * 根据手机号和密码查询用户是否匹配
     * @param mobile
     * @param password
     * @return
     */
    public User findUserByMobileAndPassword(String mobile, String password){
        //根据手机号查询用户
        User user = userDao.findByMobile(mobile);
        if(user != null && encoder.matches(password, user.getPassword())){
            return user;
        }else{
            return null;
        }
    }


UserController.java


	/**
	 * 用户登录的方法
	 * @param loginMap
	 * @return
	 */
	@RequestMapping(value = "/login", method = RequestMethod.POST)
	public Result login(@RequestBody Map loginMap){
		User user = userService.findUserByMobileAndPassword(loginMap.get("mobile"), loginMap.get("password"));
		if(user != null){
			return new Result(true, StatusCode.OK, "登录成功");
		}else{
			return new Result(false, StatusCode.LOGINERROR, "用户名或者密码错误");
		}
	}


二、常见的认证机制


1、HTTP Basic Auth


        HTTP Basic Auth简单点说明就是每次请求API时都提供用户的username和password。简言之,Basic Auth是配合RESTful API 使用的最简单的认证方式,只需提供用户名密码即可,但由于有把用户名密码暴露给第三方客户端的风险,在生产环境下被使用的越来越少。因此,在开发对外开放的RESTfulAPI时,尽量避免采用HTTP Basic Auth


2、Cookie Auth


        Cookie认证机制就是为一次请求认证在服务端创建一个Session对象,同时在客户端的浏览器端创建了一个Cookie对象;通过客户端带上来Cookie对象来与服务器端的session对象匹配来实现状态管理的。默认的,当我们关闭浏览器的时候,cookie会被删除。但可以通过修改cookie 的expire time使cookie在一定时间内有效;


微服务项目 -- day06 密码加密与微服务鉴权JWT_第2张图片


3、OAuth


        OAuth(开放授权)是一个开放的授权标准,允许用户让第三方应用访问该用户在某一web服务上存储的私密的资源(如照片,视频,联系人列表),而无需将用户名和密码提供给第三方应用。


        OAuth允许用户提供一个令牌,而不是用户名和密码来访问他们存放在特定服务提供者的数据。每一个令牌授权一个特定的第三方系统(例如,视频编辑网站)在特定的时段(例如,接下来的2小时内)内访问特定的资源(例如仅仅是某一相册中的视频)。这样,OAuth让用户可以授权第三方网站访问他们存储在另外服务提供者的某些特定信息,而非所有内容下面是OAuth2.0的流程:


微服务项目 -- day06 密码加密与微服务鉴权JWT_第3张图片


        这种基于OAuth的认证机制适用于个人消费者类的互联网产品,如社交类APP等应用,但是不太适合拥有自有认证权限管理的企业应用。


4、Token Auth


(1)、Token Auth流程


使用基于 Token 的身份验证方法,在服务端不需要存储用户的登录记录。大概的流程是这样的:


        ▶ 客户端使用用户名跟密码请求登录


        ▶ 服务端收到请求,去验证用户名与密码


        ▶ 验证成功后,服务端会签发一个 Token,再把这个 Token 发送给客户端


        ▶ 客户端收到 Token 以后可以把它存储起来,比如放在 Cookie 里


        ▶ 客户端每次向服务端请求资源的时候需要带着服务端签发的 Token


        ▶ 服务端收到请求,然后去验证客户端请求里面带着的 Token,如果验证成功,就向客户端返回请求的数据


微服务项目 -- day06 密码加密与微服务鉴权JWT_第4张图片


(2)、Token Auth的优点


        支持跨域访问: Cookie是不允许垮域访问的,这一点对Token机制是不存在的,前提是传输的用户认证信息通过HTTP头传输。


        无状态(也称:服务端可扩展行):Token机制在服务端不需要存储session信息,因为Token 自身包含了所有登录用户的信息,只需要在客户端的cookie或本地介质存储状态信息。


        更适用CDN: 可以通过内容分发网络请求你服务端的所有资料(如:javascript,HTML,图片等),而你的服务端只要提供API即可。


        去耦: 不需要绑定到一个特定的身份验证方案。Token可以在任何地方生成,只要在你的API被调用的时候,你可以进行Token生成调用即可。


        更适用于移动应用: 当你的客户端是一个原生平台(iOS, Android,Windows 8等)时,Cookie是不被支持的(你需要通过Cookie容器进行处理),这时采用Token认证机制就会简单得多。


        CSRF:因为不再依赖于Cookie,所以你就不需要考虑对CSRF(跨站请求伪造)的防范。


        性能: 一次网络往返时间(通过数据库查询session信息)总比做一次HMACSHA256计算 的Token验证和解析要费时得多。


        不需要为登录页面做特殊处理: 如果你使用Protractor 做功能测试的时候,不再需要为登录页面做特殊处理。


        基于标准化:你的API可以采用标准化的 JSON Web Token (JWT). 这个标准已经存在多个后端库(.NET, Ruby, Java,Python, PHP)和多家公司的支持(如:Firebase,Google, Microsoft)。


三、基于JWT的Token认证机制实现


1、什么是JWT


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


2、JWT的组成


一个JWT实际上就是一个字符串,它由三部分组成,头部、载荷与签名。




3、

四、Java的JJWT实现JWT

五、项目中加入鉴权功能

你可能感兴趣的:(微服务项目,密码加密)