springboot整合shiro實現登錄驗證

springboot整合shiro實現登錄驗證

今天第一次接觸springboot,本來是要學習springboot和shiro整合的,但是由於springboot結構還不太瞭解,所以先來了解一下springboot。
springboot可以快速創建一個機遇spring的項目,而且讓這個項目跑起來只需要很少的配置就可以了,主要有以下核心功能:
1.獨立運行的spring項目:springboot可以以jar包的形式來運行,運行一個springboot項目我們只需要通過jar -jar xx.jar類運行。
2.Spring Boot可以內嵌Tomcat,這樣我們無需以war包的形式部署項目。
3.提供starter簡化Maven配置:使用Spring或者SpringMVC我們需要添加大量的依賴,而這些依賴很多都是固定的,這裏Spring Boot 通過starter能夠幫助我們簡化Maven配置。
4.自動配置Spring 。
5.準生產的應用監控 。
6.無代碼生成和xml配置。
下面就是開始搭建環境了
1、先建一個springboot工程

修改項目group和artifact

選擇所需要的依賴

再選擇一下工作空間就完成了

idea創建springboot項目自帶項目入口,不用自己配置

修改一下入口類,運行就能在瀏覽器裏訪問該項目了,注意要在入口類加上一個@RestController的註解

package com.jiang.springboot_shiro;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@SpringBootApplication
public class SpringbootShiroApplication {


    public static void main(String[] args) {
        SpringApplication.run(SpringbootShiroApplication.class, args);
    }
    @RequestMapping(value = "/springboot_shiro",produces = "text/plain;charset=UTF-8")//項目訪問URL
    public String index(){
        return "Hello springboot!";
    }
}

現在在瀏覽器裏面輸入localhost:8080/springboot_shiro就能看到返回的Hello springboot!

上面這種方式是把入口類作爲控制類,如果要將控制器和入口類分離,控制器的包一定要跟啓動類是同一目錄,如下:

接下來是整合shiro進行認證,代碼會在之前的代碼上有所改動

pom.xml添加需要的包

<?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>

    <groupId>com.jiang</groupId>
    <artifactId>springboot_shiro</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>springboot_shiro</name>
    <description>Demo project for Spring Boot</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.4.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>1.3.2</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <version>2.0.3.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.0.18</version>
        </dependency>
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.4.0</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
</dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>


</project>

先建一個user表,在user表中加了一列權限

由於我的是集成了mybatis的,所以要先配置springboot的配置文件

application.properties

#spring集成mybatis環境
mybatis.type-aliases-package=com.jiang.springboot_shiro.entity
#加載mybatis配置文件
mybatis.mapper-locations=classpath:mapper/*.xml
#spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/chun?useUnicode=true&characterEncoding=utf-8
spring.datasource.username=root
spring.datasource.password=

entity.java

package com.jiang.springboot_shiro.entity;

public class User {
    private String name;
    private String password;
    private String authority;

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

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

    public String getPassword() {
        return password;
    }

    public void setAuthority(String authority) {
        this.authority = authority;
    }

    public String getAuthority() {
        return authority;
    }
}

mapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.jiang.springboot_shiro.mapper.UserMapper">
    <resultMap id="user" type="User">
        <result property="name" column="name"></result>
        <result property="password" column="password"></result>
    </resultMap>
    <select id="findUser" resultType="User" resultMap="user">
        select * from user where name =#{name} and password=#{password}
    </select>

</mapper>

mapper.java

package com.jiang.springboot_shiro.mapper;

import com.jiang.springboot_shiro.entity.User;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface UserMapper {
    public User findUser(User user);
}

service.java

package com.jiang.springboot_shiro.service;

import com.jiang.springboot_shiro.entity.User;
import com.jiang.springboot_shiro.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserService {
    @Autowired
    private UserMapper userMapper;

    public User findUser(User user){
        return userMapper.findUser(user);
    }
}

controller.java

這裏有要注意的地方,在前面搭建springboot框架的時候註釋是@RestController,這裏要訪問頁面,要把註釋改成@Controller

package com.jiang.springboot_shiro.controller;

import com.jiang.springboot_shiro.entity.User;
import com.jiang.springboot_shiro.service.UserService;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@Controller
public class UserController {
    /*@RequestMapping("springboot_shiro")//項目訪問URL
    public String index(){
        return "Hello springboot!";
    }*/
    @Autowired
    private UserService userService;
    @RequestMapping("/")
    public String getIndex(){
        return "login";
    }
    @RequestMapping("all")
    public String getAll(){
        return "all";
    }
    @RequestMapping("one")
    public String getOne(){
        return "one";
    }
    @RequestMapping("login")
    public String login(){
        return "login";
    }
    @RequestMapping("permission")
    public String permission(){
        return "permission";
    }
    @RequestMapping("toLogin")
    public String toLogin(User user, Model model){
        Subject subject=SecurityUtils.getSubject();
        UsernamePasswordToken userToken=new UsernamePasswordToken(user.getName(),user.getPassword());
        try{
            subject.login(userToken);
            return "redirect:/all";
        }catch (UnknownAccountException e){
            model.addAttribute("msg","用戶名不存在");
            return "login";
        }catch (IncorrectCredentialsException e){
            model.addAttribute("msg","密碼錯誤");
            return "login";
        }
    }

}

HTML頁面就不貼了,只有一個登陸表單,隨便寫個就行了

最重要的是shiro的配置,這裏沒有用配置文件,用了註解

shiroConfig.java

package com.jiang.springboot_shiro.shiro;

import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.HashMap;
import java.util.Map;

@Configuration
public class ShiroConfig {
    @Bean(name = "shiroFilterFactoryBean")
    public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("defaultWebSecurityManager")DefaultWebSecurityManager defaultWebSecurityManager){
        ShiroFilterFactoryBean shiroFilterFactoryBean=new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);
        Map<String,String> fMap=new HashMap<>();
        //攔截頁面
        fMap.put("/all","authc");
        fMap.put("/one","authc");
        //攔截未授權
        fMap.put("/all","perms[user:all]");
        fMap.put("/one","perms[user:one]");
        //被攔截返回登錄頁面
        shiroFilterFactoryBean.setLoginUrl("/login");
        //授權攔截返回頁面
        shiroFilterFactoryBean.setUnauthorizedUrl("/permission");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(fMap);
        return shiroFilterFactoryBean;
    }
    @Bean(name = "defaultWebSecurityManager")
    public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm")UserRealm userRealm){
        DefaultWebSecurityManager defaultWebSecurityManager=new DefaultWebSecurityManager();
        defaultWebSecurityManager.setRealm(userRealm);
        return defaultWebSecurityManager;
    }
    @Bean(name = "userRealm")
    public UserRealm getUserRealm(){
        return new UserRealm();
    }
}

配置文件裏有一些攔截的配置,anon,authc等,這些還有點糊塗。

shiro最重要的還是Realm類,自定義UserRealm需要繼承AuthorizingRealm類,並重寫其中的doGetAuthenticationInfo方法和doGetAuthorizationInfo方法,其中在controller中調用subject.login(token)時最終是會調用doGetAuthenticationInfo(token)方法,當訪問的頁面需要鑑權的時候會調用doGetAuthorizationInfo(principle)方法。

我自定義的UserRealm.java

package com.jiang.springboot_shiro.shiro;

import com.jiang.springboot_shiro.entity.User;
import com.jiang.springboot_shiro.service.UserService;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;

public class UserRealm extends AuthorizingRealm {
    @Autowired
    private UserService userService;
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection arg0){
        System.out.println("授權");
        Subject subject=SecurityUtils.getSubject();
        User user=(User) subject.getPrincipal();
        SimpleAuthorizationInfo simpleAuthorizationInfo=new SimpleAuthorizationInfo();
        simpleAuthorizationInfo.addStringPermission(user.getAuthority());
        return simpleAuthorizationInfo;
    }
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken arg0) throws AuthenticationException {
        // TODO Auto-generated method stub
        System.out.println("認證");

        //shiro判斷邏輯
        UsernamePasswordToken user = (UsernamePasswordToken) arg0;
        User realUser = new User();
        realUser.setName(user.getUsername());
        realUser.setPassword(String.copyValueOf(user.getPassword()));
        User newUser = userService.findUser(realUser);
        //System.out.println(user.getUsername());

        if(newUser == null){
            //用戶名錯誤
            //shiro會拋出UnknownAccountException異常
            return null;
        }

        return new SimpleAuthenticationInfo(newUser,newUser.getPassword(),"");
    }

}

這個類還需要改正,因爲這個認證的方法裏,無論如何都只能返回UnknownAccountException異常,controller中永遠都catch不到IncorrectCredentialsException,目前怎麼更正我還沒想好。

因爲沒有配置默認 訪問路徑,只要訪問localhost:8080/就能訪問該項目了,在我的控制器裏面我後來又加上了下面這一段代碼,如果沒有這一段代碼我直接訪問返回的是404,但是這樣程序已經跑起來了,如果輸入localhost:8080/login會發現能夠訪問的。

@RequestMapping("/")
public String getIndex(){
    return "login";
}

只是個測試,所以沒寫首頁,直接訪問登錄頁面了。

 

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