迎着风跑  

一、项目结构

结合vue-cli的知识创建k15-manager项目结构

 

 二、登录组件

​ 安装elementui

npm i element-ui -S

在App.vue中添加如下内容

import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';

Vue.use(ElementUI);

使用elementui完成登录页面

 

 

三、首页组件

 

 

 

 

四、搭建服务端项目

1、搭建springboot项目

 

 

2、所需坐标


<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
   <modelVersion>4.0.0</modelVersion>

   <packaging>jar</packaging>
   <groupId>cn.woniu</groupId>
   <artifactId>k15_manager</artifactId>
   <version>1.0-SNAPSHOT</version>

   <parent>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-parent</artifactId>
       <version>2.3.7.RELEASE</version>
   </parent>

   <properties>
       <mysql.version>8.0.22</mysql.version>
       <lombok.version>1.18.20</lombok.version>
       <mybaits.springboot.version>2.2.0</mybaits.springboot.version>
       <shiro.springboot.version>1.7.1</shiro.springboot.version>
       <fastjson.version>1.2.15</fastjson.version>
       <jjwt.version>0.9.1</jjwt.version>
       <jwt.version>3.10.3</jwt.version>
       <pagehelper.springboot.version>1.3.0</pagehelper.springboot.version>
       <mybaits.generator.version>1.3.7</mybaits.generator.version>
   </properties>

   <dependencies>
       <!--mvc相关-->
       <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter-web</artifactId>
       </dependency>
       <!--数据源-->
       <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter-jdbc</artifactId>
       </dependency>
       <!--springboot启动器-->
       <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter</artifactId>
       </dependency>
       <!--测试包-->
       <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-starter-test</artifactId>
       </dependency>
       <!--springboot整合mybaits-->
       <dependency>
           <groupId>org.mybatis.spring.boot</groupId>
           <artifactId>mybatis-spring-boot-starter</artifactId>
           <version>${mybaits.springboot.version}</version>
       </dependency>
       <!--热部署插件-->
       <dependency>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-devtools</artifactId>
           <scope>runtime</scope>
           <optional>true</optional>
       </dependency>
       <!--mysql驱动-->
       <dependency>
           <groupId>mysql</groupId>
           <artifactId>mysql-connector-java</artifactId>
           <version>${mysql.version}</version>
           <scope>runtime</scope>
       </dependency>
       <!--pagehelper插件-->
       <dependency>
           <groupId>com.github.pagehelper</groupId>
           <artifactId>pagehelper-spring-boot-starter</artifactId>
           <version>${pagehelper.springboot.version}</version>
       </dependency>
       <dependency>
           <groupId>org.mybatis.generator</groupId>
           <artifactId>mybatis-generator-core</artifactId>
           <version>${mybaits.generator.version}</version>
       </dependency>

       <!--lombok-->
       <dependency>
           <groupId>org.projectlombok</groupId>
           <artifactId>lombok</artifactId>
           <version>${lombok.version}</version>
       </dependency>
       <!--shiro-->
       <dependency>
           <groupId>org.apache.shiro</groupId>
           <artifactId>shiro-spring-boot-web-starter</artifactId>
           <version>${shiro.springboot.version}</version>
       </dependency>
       <dependency>
           <groupId>com.alibaba</groupId>
           <artifactId>fastjson</artifactId>
           <version>${fastjson.version}</version>
       </dependency>
       <!--jwt-->
       <dependency>
           <groupId>io.jsonwebtoken</groupId>
           <artifactId>jjwt</artifactId>
           <version>${jjwt.version}</version>
       </dependency>
       <dependency>
           <groupId>com.auth0</groupId>
           <artifactId>java-jwt</artifactId>
           <version>${jwt.version}</version>
       </dependency>
   </dependencies>

   <build>
       <plugins>
           <!--maven编译插件-->
           <plugin>
               <groupId>org.apache.maven.plugins</groupId>
               <artifactId>maven-compiler-plugin</artifactId>
               <version>3.8.1</version>
               <configuration>
                   <source>1.8</source>
                   <target>1.8</target>
                   <encoding>UTF-8</encoding>
               </configuration>
           </plugin>

           <!--mybatis generator自动生成配置-->
           <plugin>
               <groupId>org.mybatis.generator</groupId>
               <artifactId>mybatis-generator-maven-plugin</artifactId>
               <version>1.3.7</version>
               <configuration>
                   <!--配置文件的位置-->
                   <configurationFile>src/main/resources/generatorConfig.xml</configurationFile>
                   <verbose>true</verbose>
                   <overwrite>true</overwrite>
               </configuration>
               <dependencies>
                   <dependency>
                       <groupId>mysql</groupId>
                       <artifactId>mysql-connector-java</artifactId>
                       <version>${mysql.version}</version>
                   </dependency>
               </dependencies>
           </plugin>

           <!--springBoot插件-->
           <plugin>
               <groupId>org.springframework.boot</groupId>
               <artifactId>spring-boot-maven-plugin</artifactId>
               <version>2.3.7.RELEASE</version>
           </plugin>
       </plugins>
   </build>
</project>

3、创建统一异常处理类


/**
* 全局异常处理
*/
@RestControllerAdvice
public class GlobalException {

   //没有权限操作
   @ExceptionHandler(AuthorizationException.class)
   public ResponseResult<Void> noAnauthorized(Exception ex){
       return ResponseResult.FORBIDDEN;
  }
}

4、创建统一消息返回类


package cn.woniu.utils;

/**
* 统一消息返回类
* @param <T>
*/
public class ResponseResult<T> {
   private int code; // 状态码 200,成功,500:失败,403:无权
   private String msg; // 消息
   private T data; // 数据

   public ResponseResult() {
  }

   public ResponseResult(T data,int code) {
       this(code, "OK");
       this.data = data;
  }

   public ResponseResult(int code, String msg) {
       this.code = code;
       this.msg = msg;
  }

   public static final ResponseResult<Void> SUCCESS = new ResponseResult<>(200, "OK");
   public static final ResponseResult<Void> NOTLOGINED = new ResponseResult<>(401, "未登录");
   public static final ResponseResult<Void> FORBIDDEN = new ResponseResult<>(403, "无权限");
   public static final ResponseResult<Void> Unauthenticated = new ResponseResult<>(402, "认证失败");
   public static final ResponseResult<Void> FAIL = new ResponseResult<>(500, "操作失败");

   public int getCode() {
       return code;
  }

   public void setCode(int code) {
       this.code = code;
  }

   public String getMsg() {
       return msg;
  }

   public void setMsg(String msg) {
       this.msg = msg;
  }

   public T getData() {
       return data;
  }

   public void setData(T data) {
       this.data = data;
  }
}

 

五、实现登录功能

1、登录功能流程分析

登录功能步骤分析:

  • 根据用户名到数据库中查询用户信息

  • 进行密码匹配

  • 使用shiro对登录用户做认证,登录成功后创建token

    合使用springboot+shiro+jwt实现

2、创建自定义realm


/**
* 自定义realm,用来使用jwt做认证
*/
public class CustomRelm extends AuthorizingRealm {
   @Autowired
   private IUserService userService;
   /**
    * 授权
    * @param principalCollection
    * @return
    */
   @Override
   protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
       return null;
  }

   /**
    * 认证
    * @param authenticationToken
    * @return
    * @throws AuthenticationException
    */
   @Override
   protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
       //获得用户名
       String username= (String) authenticationToken.getPrincipal();
       //根据用户名到数据库中查询,查到数据shiro再做密码验证
       User user=userService.getUserByUserName(username);
       if(user==null){//用户名不存在
           return null;
      }

       SimpleAuthenticationInfo simpleAuthenticationInfo=new SimpleAuthenticationInfo(username, user.getPassword(),this.getName());
       //将用户权限保存到
       return simpleAuthenticationInfo;
  }
}

3、创建shiro配置类


@Configuration
public class ShiroConfig {
   //创建realm对象 需要自定义realm类
   @Bean
   public CustomRelm customRelm(){
       return new CustomRelm();
  }

  /**
    * 创建安全管理器
    * @param realm
    * @return
    */
   @Bean
   public DefaultWebSecurityManager securityManager(JwtRealm realm) {
       DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
       manager.setRealm(realm);
       return manager;
  }

    /**
    * 创建shiro过滤器工厂
    * @param securityManager
    * @return
    */
   @Bean(name ="shiroFilterFactoryBean" )
   public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager securityManager) {
       ShiroFilterFactoryBean shiroFilterFactoryBean=new ShiroFilterFactoryBean();
       shiroFilterFactoryBean.setSecurityManager(securityManager);

       //配置过滤器拦截链
       Map<String, String> maps = shiroFilterFactoryBean.getFilterChainDefinitionMap();
       maps.put("/user/login", "anon");//匿名过滤器
       //maps.put("/**", "authc");//认证过滤器

       //所有的请求都必须经过认证过滤器,如果没有登录,让程序跳到登录页面进行认证
       //当在factoryBean设置了要去登录请求的路径是setLoginUrl() 此时过滤器不拦截
       shiroFilterFactoryBean.setLoginUrl("/user/login");
       return shiroFilterFactoryBean;
  }
}

4、Controller的登录处理


@RestController
@RequestMapping("/user")
@CrossOrigin
public class UserController {
   @Autowired
   private IUserService userService;

   /**
    * 登录功能
    *
    * @param user
    * @return
    */
   @PostMapping("/login")
   public ResponseResult<Object> getLogin(@RequestBody User user) throws Exception {
       //获得Shiro的Subject对象
       Subject subject= SecurityUtils.getSubject();
       //获得密码管理器
       UsernamePasswordToken usernamePasswordToken=new UsernamePasswordToken(user.getUsername(),user.getPassword());
       try {
           //调用shiro的登录方法
           subject.login(usernamePasswordToken);
      }catch (UnknownAccountException ex){
           return new ResponseResult<Object>(202, "用户不存在");
      }catch (IncorrectCredentialsException ex){
           return new ResponseResult<Object>(202, "密码错误");
      }

       //生成tocken
       String tocken = JwtTokenUtils.createSign(user.getUsername());
       return new ResponseResult<Object>(200, user.getUsername(), tocken);
  }
}

5、登录组件处理

 

六、讲师列表组件

 

 

 

七、Shiro整合JWT实现认证

<font color=red>提示:在前后端分离的项目中,客户端发送级服务器的请求除了登录请求外,其余请求都要将登录时获得token传给服务端</font>

1、讲师列表关键代码

 

 

 

2、Shiro整合JWT

a、 创建jwt过滤器


/**
* 自定义过滤器,用来自动处理页面发送的token
*/
public class JwtAuthenticationFilter extends AccessControlFilter {
   @Override
   protected boolean isAccessAllowed(ServletRequest servletRequest, ServletResponse servletResponse, Object o) throws Exception {
       return true;
  }

   /**
    * 解析tokcn
    * @param servletRequest
    * @param servletResponse
    * @return
    * @throws Exception
    */
   @Override
   protected boolean onAccessDenied(ServletRequest servletRequest, ServletResponse servletResponse) throws Exception {
       HttpServletRequest request= (HttpServletRequest) servletRequest;
        HttpServletRequest request1 = (HttpServletRequest) request;
     //预检:在跨域请求中,如果是一个复杂类型(Option)的请求,这个时候,前端浏览器会先发一个请求 试探一下,如果后台允许 这个时候
       //才会真正的发送请求 以及请求的参数 和请求头的数据 所以一共有2次请求
       if(HttpMethod.OPTIONS.toString().equals(request.getMethod())){
           return true;
      }
       
        try {
            /*解析token 在shiro自定义realm的认证中实现
            * 思路:拿到shiro的subject去调用login()
            * */
           //获得客户端传过来的token
             String authorzation=request.getHeader("Authorzation");
            UsernamePasswordToken token=new UsernamePasswordToken(authorzation,authorzation);
           this.getSubject(request,response).login(token);
      }catch (JWTVerificationException exception){
           System.out.println("jwtstr 非法");
           return false;
      }
       return true;
  }
}      

<font color=red>提示:注意跨域请求的预检问题</font>

b、修改shiro配置类


@Bean(name ="shiroFilterFactoryBean" )
   public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager securityManager) {
       ShiroFilterFactoryBean shiroFilterFactoryBean=new ShiroFilterFactoryBean();
       shiroFilterFactoryBean.setSecurityManager(securityManager);

       //获得jwt自定义过滤器
       Map<String, Filter> filteMap=shiroFilterFactoryBean.getFilters();
       //注册自定认jwt过滤器到shiro的过滤器工厂中
       filteMap.put("jwtFilter", new JwtAuthenticationFilter());

       //配置过滤器拦截链
       Map<String, String> maps = shiroFilterFactoryBean.getFilterChainDefinitionMap();
       maps.put("/user/login", "anon");//匿名过滤器
       maps.put("/**", "jwtFilter");//非登录请求全都使用jwt的认证过滤器
       return shiroFilterFactoryBean;
  }

c、修改自定义Relm


/**
* 自定义realm,用来使用jwt做认证
*/
public class JwtRealm extends AuthorizingRealm {
   @Autowired
   private IUserService userService;

   /**
    * 授权
    *
    * @param principalCollection
    * @return
    */
   @Override
   protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
      return null;
  }

   /**
    * 认证
    *
    * @param authenticationToken
    * @return
    * @throws AuthenticationException
    */
   @Override
   protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
        //获得jwtToken中的帐(即是帐号也是密码) 第一个参数
       String jwtName = authenticationToken.getPrincipal().toString();
       //获得jwtToken中和密码 第二个参数
       String jwtPwd =new String((char[]) authenticationToken.getCredentials());

       //通过什么判断是执行登录认证还是token认证
       //这个方法是shiro的认证方法,只能拿到shiro的token对象,无法获得jwt的token对象的
       if (jwtName.equals(jwtPwd)) {
           //是从jwt的token中获得的值,非登录请求,调用验证token的方法
           JwtUtils.checkSign(jwtName);
           //将token信息存到shiro的认证消息对象中
           SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(jwtName, jwtPwd, this.getName());
           return authenticationInfo;
      }

       //根据用户名查询
       Users users = usersService.getUserByAccount(jwtName);
       if (users == null) {
           return null;
      }
       SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(users.getUsername(), users.getPassword(), this.getName());
       return simpleAuthenticationInfo;
  }
}

 

posted on 2021-10-28 17:56  迎着风跑  阅读(140)  评论(0编辑  收藏  举报