SSM(Spring+Springmvc+MyBatis)+ajax+JWT,实现登录JWT(token)认证获取权限

 本人最近钻研SpringMVC,都是学习感悟与个人看法,接受任何提问与建议,希望大家提出自己独特的见解,谢谢。

也可以进入我的b站主页,观看在线教程,视频中写出了当前文章中未出现的技术,而且在拦截器上也做出了许多改动,使用了两个拦截器来进行认证与权限;

微风梦之雫的个人空间_哔哩哔哩_bilibili

1-总体

2-首先是我们的pom.xml

<build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>8</source>
                    <target>8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
    <packaging>war</packaging>
<dependencies>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>5.3.5</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.3.5</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.3.5</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.springframework/spring-core -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>5.3.5</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-beans -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>5.3.5</version>
        </dependency>


        <dependency>
            <groupId>org.glassfish.web</groupId>
            <artifactId>jstl-impl</artifactId>
            <version>1.2</version>
            <!-- 去除依赖 -->
            <exclusions>
                <exclusion>
                    <groupId>javax.servlet</groupId>
                    <artifactId>servlet-api</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.76</version>
        </dependency>
        <dependency>
            <groupId>c3p0</groupId>
            <artifactId>c3p0</artifactId>
            <version>0.9.1.2</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.20</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.3.5</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-websocket -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-websocket</artifactId>
            <version>5.3.5</version>
        </dependency>


        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>1.3.0</version>
        </dependency>

        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.4.1</version>
        </dependency>

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.25</version>
        </dependency>

        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.0</version>
        </dependency>
        <!--redis -->

        <!--spring-data-redis版本不能太高害怕有冲突 -->

        <dependency>

            <groupId>org.springframework.data</groupId>

            <artifactId>spring-data-redis</artifactId>

            <version>1.7.5.RELEASE</version>

        </dependency>

        <dependency>

            <groupId>redis.clients</groupId>

            <artifactId>jedis</artifactId>

            <version>2.9.0</version>

        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.11.4</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/commons-codec/commons-codec -->
        <dependency>
            <groupId>commons-codec</groupId>
        <artifactId>commons-codec</artifactId>
        <version>1.10</version>
        </dependency>

    </dependencies>

3-Configuration配置类

1.ServletConfig, 我们的一些拦截器,或者处理器,视图解析器,都可以在此配置类进行配置,它是实现了WebMvcConfigurer的方法;

@Configuration
@EnableWebMvc
@ComponentScan(basePackages = "com.cjj.controller")
public class ServletConfig implements WebMvcConfigurer {

    @Bean
    public ViewResolver viewResolver(){
        InternalResourceViewResolver viewResolver=new InternalResourceViewResolver();
        viewResolver.setPrefix("/WEB-INF/views/");
        viewResolver.setSuffix(".html");
        viewResolver.setExposeContextBeansAsAttributes(true);
        return viewResolver;
        //InternalResourceViewResolver 是 Spring MVC 中最常用的视图解析器
        // 它用于解析 JS P或 HTML 等资源文件。该解析器会将逻辑视图名称加上前缀和后缀
        // 例如将逻辑视图名称 “hello” 解析为 “/WEB-INF/views/hello.html”。
    }
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }
    @Bean
    public MyInterceptor getMyInterceptor(){
        return new MyInterceptor();
    }
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(getMyInterceptor())
                .addPathPatterns("/**")
                .excludePathPatterns("/login2")
                .excludePathPatterns("/")
                .excludePathPatterns("/testredis")
                .excludePathPatterns("/image/**")
                .excludePathPatterns("/js/**")
                .excludePathPatterns("/css/**");
    }
}

2.RootConfig,用于配置我的数据库,完全实现用注解,xml文件不需要使用;

@Configuration
// Reposity  @service  @Bean
@MapperScan(basePackages="com.cjj.Dao")

@ComponentScan(basePackages = "com.cjj" ,excludeFilters =
        {@ComponentScan.Filter(type = FilterType.ANNOTATION,value ={EnableWebMvc.class, Controller.class})})
//是除了web层的所有的Bean
@PropertySource({"classpath:jdbc.properties"})
public class RootConfig {

    @Value("${driverClass}")
    private  String driver;
    @Value("${url}")
    private String url;

    @Value("${root}")
    private String root;

    @Value("${password}")
    private String password;

    @Bean(name="datasource")
    public DataSource getDataSource(){

        ComboPooledDataSource dataSource=new ComboPooledDataSource();
        try {
            dataSource.setDriverClass(driver);
            dataSource.setJdbcUrl(url);
            dataSource.setUser(root);
            dataSource.setPassword(password);
        } catch (PropertyVetoException e) {
            e.printStackTrace();
        }
        return dataSource;
    }


    @Bean(name = "jdbctemplate")
    public JdbcTemplate getJdbcTemplate(DataSource dataSource){
        JdbcTemplate jdbcTemplate=new JdbcTemplate(dataSource);
        return jdbcTemplate;
    }

    //mybatis的配置
    @Bean
    public SqlSessionFactoryBean sqlSessionFactoryBean() throws IOException {
        System.out.println("SqlSessionFactoryBean---------------------------");
        ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();//mybatis-plus插件类
        sqlSessionFactoryBean.setDataSource(getDataSource());//数据源
        sqlSessionFactoryBean.setMapperLocations(resourcePatternResolver.getResources("classpath:mappers/*.xml"));
        return sqlSessionFactoryBean;
    }
}

让我们可以不需要配置xml文件,就能访问到mappers下的所有映射文件,jdbc.properties我就不献上了,大家根据自己的数据库配置来;

3. MvcApplication,实现我们的AbstractAnnotationConfigDispatcherServletInitializer,就不用多说了,用于配置我们的容器,很关键的一个容器类;

public class MvcApplication extends AbstractAnnotationConfigDispatcherServletInitializer {
    @Override
    protected Class<?>[] getRootConfigClasses() {
        //返回的带有@Configuration注解的类用来配置ContextLoaderListener;
        System.out.println("RootConfig-----------");
        return new Class[]{RootConfig.class};
    }

    @Override
    protected Class<?>[] getServletConfigClasses() {
        //将一个或多个路径映射到DispatcherServlet上;
        System.out.println("ServletConfig-----------");
        return new Class[]{ServletConfig.class};
    }

    @Override
    protected String[] getServletMappings() {
        // /*拦截所有http请求,包括.jsp,都作为控制器类的请求路径处理;
        System.out.println("getServletMappings-----------");
        return new String[]{"/"};
    }

}

4- 创建Model实体类

配置全都部署好了,接下来开始我们的实体类

User

@Component
public class User {
    private int id;
    private String username;
    private String password;
    private String token;
    private int rid;
    private List<privilege> pprivilegerList;

    public User() {
    }

    public User(String username, String password, String token) {
        this.username = username;
        this.password = password;
        this.token = token;
    }

    public User(int id, String username, String password, String token, int rid) {
        this.id = id;
        this.username = username;
        this.password = password;
        this.token = token;
        this.rid = rid;
    }
    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getToken() {
        return token;
    }

    public void setToken(String token) {
        this.token = token;
    }



    public int getRid() {
        return rid;
    }

    public void setRid(int rid) {
        this.rid = rid;
    }

    public List<privilege> getPprivilegerList() {
        return pprivilegerList;
    }

    public void setPprivilegerList(List<privilege> pprivilegerList) {
        this.pprivilegerList = pprivilegerList;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", token='" + token + '\'' +
                ", rid=" + rid +
                '}';
    }
}

 Privilege,我的权限是用来测试的,我也不清楚,我的用法对不对,希望大家指点;

@Component
public class privilege {
    private int id;
    private String demoname;
    private int pid;

    public privilege() {
    }

    public privilege(int id, String demoname, int pid) {
        this.id = id;
        this.demoname = demoname;
        this.pid = pid;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getDemoname() {
        return demoname;
    }

    public void setDemoname(String demoname) {
        this.demoname = demoname;
    }

    public int getPid() {
        return pid;
    }

    public void setPid(int pid) {
        this.pid = pid;
    }
}

5- 接口,实现映射

@Repository
@Mapper
public interface UserDao {
    public User login(User user);

    public List<privilege> getprivilege(User user);
}
<mapper namespace="com.cjj.Dao.UserDao">
    <select id="login" parameterType="com.cjj.Model.User" resultType="com.cjj.Model.User">
        select id,username,password,token,rid FROM `user` where `user`.username=#{username} and `user`.`password`=#{password}
    </select>
    <resultMap id="wei" type="com.cjj.Model.privilege" >
            <id column="id" property="id"/>
            <result column="demoname" property="demoname"/>
            <result column="rid" property="rid"/>
    </resultMap>
    <select id="getprivilege" parameterType="com.cjj.Model.User" resultMap="wei">
        SELECT a.id ,b.demoname ,b.pid  FROM user
        INNER JOIN role ON role.id = user.rid
        INNER JOIN privilege a ON a.id = role.id
        INNER JOIN privilege b on a.id = b.pid
        WHERE user.rid= #{rid}
    </select>
</mapper>

6-创建业务方法,service

public interface UserServiceIdao {
    public User login(User user);

    public List<privilege> getprivilege(User user);
}

 实现接口

@Service
public class UserService implements UserServiceIdao {
    @Autowired
    private UserDao userDao;

    @Override
    public User login(User user) {
        return userDao.login(user);
    }

    @Override
    public List<privilege> getprivilege(User user) {
        return userDao.getprivilege(user);
    }
}

7-JwtBuilder

在咱们进入控制层之前,咱们先来配置好jwt类的生成与解析

public class JWTuuid {
    private static final long time =60*60*24*1000;
    private static final String signature="weifeng00000x";//秘钥

    public  static final String createjwt(String name, User user)
    {
        JwtBuilder jwtBuilder= Jwts.builder();
        String jwttoken=jwtBuilder
                //header -- 设置头
                .setHeaderParam("typ","JWT")
                .setHeaderParam("alg","HS256")//加密算法

                //payload----载荷
                .claim("username",name)
                .claim("stu", user)
                //主题
                .setSubject("weifeng-test")
                //有效时间
                .setExpiration(new Date(System.currentTimeMillis()+time))//从当前开始,24小时
                //jwt id
                .setId(UUID.randomUUID().toString())
                //签名 signature
                .signWith(SignatureAlgorithm.HS256,signature)
                .compact();
            return jwttoken;
    }
    // 解析 JWT
    public static Claims testJWT(String jwt) {
        try{
            Claims claims = Jwts.parser()
                    .setSigningKey(signature) //放入秘钥
                    .parseClaimsJws(jwt) //放入需要解析的串
                    .getBody();
            return claims;
        }catch (Exception e){
            e.printStackTrace();
//             如果对于秘钥解析错误那么放回null
            return null;
        }
    }

    public static boolean validateToken(String token) {
        try {
            Jwts.parser().setSigningKey(signature).parseClaimsJws(token);
            return true;
        } catch (Exception e) {
            return false;
        }
    }

}

8-Controller控制层

终于来到了咱们的控制层

@RestController
public class UserController {

    @Autowired
    UserService userService;

    @CrossOrigin(origins = "http://localhost:63342")
    @PostMapping("/login2")
    @ResponseBody
    public Object login2(@RequestBody JSONObject json, HttpServletRequest req, HttpServletResponse response)
    {
            String username=json.getString("username");
            String password=json.getString("password");
            User user =new User(username,password,"");
            System.out.println("username  "+username+"-----pwd  "+password);
            User login = userService.login(user);
            System.out.println("login的值为---------"+login);
            if(login!=null)
            {
                req.getSession().setAttribute("user",login);
                String createjwt = JWTuuid.createjwt(username, user);
                user.setToken(createjwt);
                System.out.println("jwt码--"+createjwt);

                // 将token写入响应头中
                response.addHeader("Authorization", "Bearer " + createjwt);
                response.setContentType("application/json;charset=utf-8");
                return createjwt;
            }
            else {
                String error="用户名或密码错误";
                // 设置HTTP状态码为401 Unauthorized
                response.setStatus(HttpStatus.UNAUTHORIZED.value(),"用户名或密码错误");
                //response.setStatus(HttpStatus.UNAUTHORIZED.value());
                response.setContentType("application/json;charset=utf-8");
                // 返回
                return error;
            }
    }

    @RequestMapping(value = "/aa",method = RequestMethod.GET)
    public ModelAndView getaa(HttpServletRequest req)
    {
        System.out.println("进来这个方法 getaa()");
        ModelAndView mv=new ModelAndView();
        mv.setViewName("aa");

        return mv;
    }

    @GetMapping("/demo")
    @ResponseBody
    public Object getstu(@RequestParam("demo") int demo,HttpServletRequest req){
        System.out.println("进来这个方法 getstu()");
        System.out.println("demo : "+demo);
//        System.out.println("token:  "+token);
//        Claims claims=JWTuuid.testJWT(token);
//        if(claims==null){
//            System.out.println("jwt码有误,勿扰");
//            return "jwt码有误,勿扰";
//        }
        List<privilege> user = userService.getprivilege((User) req.getSession().getAttribute("user"));
        for (privilege s : user) {
            System.out.println(s.getDemoname());
        }
//        String obj=claims.getSubject();
//        System.out.println("提交主题:"+obj);
        return user;
    }


}

其实我的登录是有些问题的,我返回的直接是个字符串,照理说应该是一个result集合,然后给状态或是其他什么的,将就看看吧,哈哈;

9-HandlerInterceptor拦截器

public class MyInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("===============请求处理前(1)===================");
        String path=request.getRequestURL().toString();
        if(path.indexOf("/login2")>=0){
            System.out.println("确实是login方法------");
            return true;
        }
        System.out.println("--------------------------------------"+path);
        Object obj=request.getSession().getAttribute("user");
        System.out.println(obj+"---------------------MyInterceptor");
        if(obj!=null)
            return true;

        // 获取Authorization请求头中的token
        String authHeader = request.getHeader("Authorization");
        if (authHeader != null && authHeader.startsWith("Bearer ")) {
            String token = authHeader.substring(7);
            System.out.println("截取的token");
            // 验证token
            if (JWTuuid.validateToken(token)) {
                return true;  // 验证通过,继续执行后续操作
            }
        }

        //request.setAttribute("msg","还没有登陆,请先登录");
        request.getRequestDispatcher("index.html").forward(request,response);
        return false;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
//        HandlerInterceptor.super.postHandle(request, response, handler, modelAndView);
        System.out.println("===============请求处理后,生成视图之前(2)===================");
        //assert modelAndView != null;
//        System.out.println("modelviewname============="+modelAndView.getViewName());
    }


    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
//        HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
        System.out.println("===============结束时调用(3)===================");
    }
}

我觉得我的拦截器,写的有问题,虽然能成功使用,但还是希望大家提出各自独特的见解;

10-前端界面

index.html


<html>
<head>
    <meta charset="UTF-8">
    <title>$Title$</title>
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
</head>
<body>
    用户:<input id="username" type="text" name="username" placeholder="请输入用户">
    密码:<input id="password" type="password" name="password" placeholder="请输入密码">
    <button id="login" value="登录"/>
    登录
    <button type="reset" value="重置"/>
    重置


<script>
    $("#login").click(function (){
        let username = document.getElementById("username").value;
        let password = document.getElementById("password").value;
        let user={};
        user.username=username;
        user.password=password;
        console.log(user);
        $.ajax({
            url: 'http://localhost:9090/login2',  // 请求的API接口地址
            //http://iwenwiki.com:8088/api/FingerUnion/list.php
            type: 'POST',
            // headers:{
            //     token:"wawa"
            // },
            dataType: 'text',
            contentType:"application/json",
            data:JSON.stringify(user) ,
            success: function (e) {
                alert("key为:"+e);
                console.log("key为:"+e);
                localStorage.setItem('token', e);
                sessionStorage.setItem('token',e);
                window.location.href="http://localhost:9090/aa";
                // TODO: 处理接收到的数据
            },
            error: function (e) {
                alert("账号或密码错误!!");
            }
        });
    });

    // let p=new Promise((resolve, reject) =>{
</script>
</body>
</html>

aa.html    拿到当前用户的操作权限的

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
</head>
<body>
<p id="pp">你好你好你好你好喝牛奶</p>
操作功能:<button type="button" id="caoz" value="请操作"/>


</body>
</html>
<script>
    $("#caoz").click(function (){
        $.ajax({
            url: 'http://localhost:9090/demo',  // 请求的API接口地址
            type:"get",
            dataType: 'json',
            contentType:"application/json",
            data:{
                demo:20040309,
            },
            headers:{
                "Authorization":localStorage.getItem('token'),
            },
            success:function (e){
                alert("success回调执行了")
                for(var i = 0; i < e.length; i++) {
                    var name = e[i].demoname;
                    console.log("权限有", name);
                }
            },
            error:function (e){
                alert("error回调执行了")
            },
        });
    })
</script>

到此,我个人学习的收获就完了,希望能看到这里的朋友,学习永无瓶颈,天天开心,一直保持着一颗追求新技术的心。

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值