十次方後端筆記六:微服務鑑權

用戶微服務

用戶微服務密碼加密存入數據庫。

密碼加密

準備工作

  1. 引入依賴

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    
  2. 安全配置類

    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. 配置加密工具類

    package com.tensquare.user.config;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
    import org.springframework.stereotype.Component;
    import util.IdWorker;
    
    /**
     * 存放所有用戶微服務的bean
     */
    @Component
    public class UserApplicationConfig {
        
        @Bean
        public BCryptPasswordEncoder bcryptPasswordEncoder(){
            return new BCryptPasswordEncoder();
        }
    
        @Bean
        public IdWorker idWorker(){
            return new IdWorker(1, 8);
        }
    
    }
    

    這裏我把UserApplication中的IdWorker的bean也放到這個類中。

管理員密碼加密

  1. 修改AdminService#add的邏輯,對原始密碼進行加密存儲

    	@Autowired
    	private BCryptPasswordEncoder encoder;	
    	/**
    	 * 增加
    	 * @param admin
    	 */
    	public void add(Admin admin) {
    		String decodePassword = idWorker.nextId()+"";
    		//密碼加密
    		admin.setPassword(encoder.encode(decodePassword));
    		adminDao.save(admin);
    	}
    

管理員登錄驗證

  1. AdminController新增方法

        /**
         * 用戶登陸
         */
        @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) {
                return new Result(true, StatusCode.OK, "登陸成功");
            }
            return new Result(false, StatusCode.LOGINERROR, "用戶名或密碼錯誤");
        }
    
  2. AdminService新增方法

    	/**
    	 * 根據登陸名和密碼查詢
    	 *
    	 * @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;
    		}
    		return null;
    	}
    
  3. AdminDao新增方法

    Admin findByLoginname(String loginname);
    

用戶密碼加密

  1. 修改UserService#add(User user, String code)方法,對原始密碼進行加密存儲

        /**
         * 增加
         *
         * @param user 用戶
         * @param code 用戶填寫的驗證碼
         */
        public void add(User user, String code) {
            //判斷驗證碼是否正確
            String syscode = (String) redisTemplate.opsForValue().get("smscode_" + user.getMobile());
            //提取系統正確的驗證碼
            if (syscode == null) {
                throw new RuntimeException("請點擊獲取短信驗證碼");
            }
            if (!syscode.equals(code)) {
                throw new RuntimeException("驗證碼輸入不正確");
            }
            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());//最後登陸日期
    
            //密碼加密
            user.setPassword(encoder.encode(user.getPassword()));
    
            userDao.save(user);
        }
    

用戶登錄驗證

  1. UserController新增方法

        /**
         * 用戶登陸
         */
        @RequestMapping(value = "/login", method = RequestMethod.POST)
        public Result login(String mobile, String password) {
            User user = userService.findByMobileAndPassword(mobile, password);
            if (user != null) {
                return new Result(true, StatusCode.OK, "登陸成功");
            }
            return new Result(false, StatusCode.LOGINERROR, "用戶名或密碼錯誤");
        }
    
  2. UserService新增方法

        /**
         * 根據手機號和密碼查詢用戶
         *
         * @param mobile
         * @param password
         * @return
         */
        public User findByMobileAndPassword(String mobile, String password) {
            User user = userDao.findByMobile(mobile);
            if (user != null && encoder.matches(password, user.getPassword())) {
                return user;
            }
            return null;
        }
    
  3. UserDao新增方法

    User findByMobile(String mobile);
    

鑑權微服務

鑑權微服務創建Module(省略)

準備工作

鑑權工具類

tensquare_common中引入JWT依賴並編寫工具類

  1. 引入JWT依賴

    <dependency>
        <groupId>io.jsonwebtoken</groupId>
        <artifactId>jjwt</artifactId>
        <version>0.6.0</version>
    </dependency>
    
  2. 編寫工具類

    package util;
    
    import io.jsonwebtoken.Claims;
    import io.jsonwebtoken.JwtBuilder;
    import io.jsonwebtoken.Jwts;
    import io.jsonwebtoken.SignatureAlgorithm;
    import lombok.Data;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    
    import java.util.Date;
    
    @Data
    @ConfigurationProperties("jwt.config")
    public class JwtUtil {
    
        private String key ;
        private long ttl ;// token有效期
    
        /**
         * 生成JWT
         *
         * @param id
         * @param subject
         * @return
         */
        public String createJWT(String id, String subject, String roles) {
            long nowMillis = System.currentTimeMillis();
            Date now = new Date(nowMillis);
            JwtBuilder builder = Jwts.builder().setId(id)
                    .setSubject(subject)
                    .setIssuedAt(now)
                    .signWith(SignatureAlgorithm.HS256, key).claim("roles", roles);
            if (ttl > 0) {
                builder.setExpiration( new Date( nowMillis + ttl));
            }
            return builder.compact();
        }
    
        /**
         * 解析JWT
         * @param jwtStr
         * @return
         */
        public Claims parseJWT(String jwtStr){
            return Jwts.parser()
                    .setSigningKey(key)
                    .parseClaimsJws(jwtStr)
                    .getBody();
        }
    }
    

管理員後臺登陸簽發token

配置Jwt工具類Bean

UserApplicationConig中配置Bean

	@Bean
    public JwtUtil jwtUtil(){
        return new JwtUtil();
    }

配置JWT常量

在用戶微服務中的application.yml中配置相關參數

jwt:
  config:
    key: imxushuai
    ttl: 360000

修改AdminController#login方法

    /**
     * 用戶登陸
     */
    @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<String, String> map = new HashMap<>();
            map.put("token", token);
            map.put("name", admin.getLoginname());//登陸名
            return new Result(true, StatusCode.OK, "登陸成功", map);
        }
        return new Result(false, StatusCode.LOGINERROR, "用戶名或密碼錯誤");
    }

統一鑑權攔截器

編寫攔截器

package com.tensquare.user.filter;

import io.jsonwebtoken.Claims;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import util.JwtUtil;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Component
public class JwtFilter extends HandlerInterceptorAdapter {

    @Autowired
    private JwtUtil jwtUtil;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        final String authHeader = request.getHeader("Authorization");
        if (authHeader != null && authHeader.startsWith("Bearer ")) {
            final String token = authHeader.substring(7);
            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;
    }
}

攔截器中只是對token進行解析,只要解析成功就放心,具體的操作權限交給具體的業務進行判斷。

比如:刪除用戶

​ 使用admin的token,在攔截器中會在Request中添加keyadmin_claims的數據,在執行到具體的controller當中,只需要判斷是否含有admin_claimskey即可,含有該key就執行邏輯,否則就拋出權限不足異常。

配置攔截器

package com.tensquare.user.config;

import com.tensquare.user.filter.JwtFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;

@Configuration
public class ApplicationConfig extends WebMvcConfigurationSupport {
    
    @Autowired
    private JwtFilter jwtFilter;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(jwtFilter).
                addPathPatterns("/**").
                excludePathPatterns("/**/login");
    }
}

用戶登錄生成token

修改UserController#login方法邏輯

    @Autowired
    private JwtUtil jwtUtil;    
	/**
     * 用戶登陸
     */
    @RequestMapping(value = "/login", method = RequestMethod.POST)
    public Result login(@RequestBody Map<String, String> loginMap) {
        User user = userService.findByMobileAndPassword(loginMap.get("mobile"), loginMap.get("password"));
        if (user != null) {
            String token = jwtUtil.createJWT(user.getId(),
                    user.getNickname(), "user");
            Map<String, String> map = new HashMap<>();
            map.put("token", token);
            map.put("name", user.getNickname());//暱稱
            map.put("avatar", user.getAvatar());//頭像
            return new Result(true, StatusCode.OK, "登陸成功", map);
        }
        return new Result(false, StatusCode.LOGINERROR, "用戶名或密碼錯誤");
    }

各微服務鑑權添加(省略)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章