记录一次基于springboot2.0.4整合shiro框架,平时没有写博客的习惯,正所谓好记性不如烂笔头。

                                    Springboot2.0.4整合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.soneer</groupId>
    <artifactId>fp</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>fp</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-validation</artifactId>
        </dependency>
        <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>

        <!-- aop -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
            <version>2.0.4.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
            <version>2.0.4.RELEASE</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>


        <!-- generator -->
        <dependency>
            <groupId>org.mybatis.generator</groupId>
            <artifactId>mybatis-generator-core</artifactId>
            <version>1.3.5</version>
        </dependency>
        
        <!--druid连接池-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.10</version>
        </dependency>

        <!-- pagehelper -->
        <dependency>
            <groupId>com.github.pagehelper</groupId>
            <artifactId>pagehelper-spring-boot-starter</artifactId>
            <version>1.2.5</version>
        </dependency>

        <!-- shiro-spring权限管理 -->
        <dependency>
            <groupId>org.apache.shiro</groupId>
            <artifactId>shiro-spring</artifactId>
            <version>1.4.0</version>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <mainClass>com.soneer.fp.FpApplication</mainClass>
                </configuration>
            </plugin>
             <!--这里我是用了逆向工程生成实体类和mapper-->
            <plugin>
                <groupId>org.mybatis.generator</groupId>
                <artifactId>mybatis-generator-maven-plugin</artifactId>
                <version>1.3.5</version>
                <configuration>
                    <verbose>true</verbose>
                    <overwrite>true</overwrite>
                </configuration>
                <dependencies>
                    <dependency>
                        <groupId>mysql</groupId>
                        <artifactId>mysql-connector-java</artifactId>
                        <version>5.1.46</version>
                    </dependency>
                </dependencies>
            </plugin>
        </plugins>


    </build>


</project>

 

application.properties

server.port=8888

# Generator逆向工程
#generator.targetProject=src/main/java
#generator.javaModel-targetPackage=com.soneer.fp.pojo
#generator.sqlMap-targetPackage=com.soneer.fp.mapper
#generator.javaClient-targetPackage=com.soneer.fp.mapper

#thymelea模板配置
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html
spring.thymeleaf.mode=HTML5
spring.thymeleaf.encoding=UTF-8
spring.thymeleaf.cache=false//开发环境设为false,生产环节根据需要去设置
spring.resources.chain.strategy.content.enabled=true
spring.resources.chain.strategy.content.paths=/**

mybatis.mapper-locations=classpath:mapper/*.xml

#durid连接池
spring.datasource.type = com.alibaba.druid.pool.DruidDataSource
spring.datasource.druid.driver-class-name = com.mysql.jdbc.Driver
spring.datasource.druid.url = jdbc:mysql://localhost:3306/sone
spring.datasource.druid.username=root
spring.datasource.druid.password=sone920801

#初始化时建立物理连接的个数,初始化发生在显示调用init方法,或者第一次getConnection时
spring.datasource.druid.initial-size=5
#最大连接池数量
spring.datasource.druid.max-active=100
#最小连接池数量
spring.datasource.druid.min-idle=5
#获取连接时最大等待时间,单位毫秒
spring.datasource.druid.max-wait=60000
#是否缓存preparedStatement,也就是PSCache。PSCache对支持游标的数据库性能提升巨大
spring.datasource.druid.pool-prepared-statements=true

spring.datasource.druid.max-pool-prepared-statement-per-connection-size=20
#用来检测连接是否有效的sql,要求是一个查询语句
spring.datasource.druid.validation-query=SELECT * FROM user

spring.datasource.druid.validation-query-timeout=60000
#申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能
spring.datasource.druid.test-on-borrow=false
#归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能
spring.datasource.druid.test-on-return=false
#建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。
spring.datasource.druid.test-while-idle=true
#	有两个含义:
#1) Destroy线程会检测连接的间隔时间
# 2) testWhileIdle的判断依据,详细看testWhileIdle属性的说明
spring.datasource.druid.time-between-eviction-runs-millis=60000
spring.datasource.druid.min-evictable-idle-time-millis=100000

# WebStatFilter配置,说明请参考Druid Wiki,配置_配置WebStatFilter
spring.datasource.druid.web-stat-filter.enabled=true
spring.datasource.druid.web-stat-filter.url-pattern=/*
spring.datasource.druid.web-stat-filter.exclusions=*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*


# StatViewServlet配置,说明请参考Druid Wiki,配置_StatViewServlet配置
spring.datasource.druid.stat-view-servlet.enabled= true
spring.datasource.druid.stat-view-servlet.url-pattern= /druid/*
spring.datasource.druid.stat-view-servlet.reset-enable= false
spring.datasource.druid.stat-view-servlet.login-username= admin
spring.datasource.druid.stat-view-servlet.login-password= admin
spring.datasource.druid.stat-view-servlet.allow= 127.0.0.1
#spring.datasource.druid.stat-view-servlet.deny= 192.168.32.139 #访问黑名单

# 配置StatFilter
spring.datasource.druid.filter.stat.db-type=mysql
spring.datasource.druid.filter.stat.log-slow-sql=true
spring.datasource.druid.filter.stat.slow-sql-millis=5000

# 配置WallFilter
spring.datasource.druid.filter.wall.enabled=true
spring.datasource.druid.filter.wall.db-type=mysql
spring.datasource.druid.filter.wall.config.delete-allow=false
spring.datasource.druid.filter.wall.config.drop-table-allow=false

#pagehelper分页
logging.level.com.soneer.fp.service=DEBUG
pagehelper.helperDialect=mysql
pagehelper.reasonable=true
pagehelper.supportMethodsArguments=true
pagehelper.params=count=countSql
pagehelper.page-size-zero=true


目录结构

这里圈出来的不用管它,pom和application文件都已经去除配置了。

cosrConfig里面是用于跨域配置的。

annotation里面是我自定义的注解。

author里面是通过aop来做api调用身份认证的(相关的依赖和配置均已去除,且不管它)。

下面进入正题!

DruidConfig类
package com.soneer.fp.datasource;


import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;


import com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceBuilder;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;

import javax.sql.DataSource;

@Configuration
@ConditionalOnClass(com.alibaba.druid.pool.DruidDataSource.class)
@ConditionalOnProperty(name = "spring.datasource.type", havingValue = "com.alibaba.druid.pool.DruidDataSource", matchIfMissing = true)
public class DruidConfig {

    /**
     * @see

     * @return DruidDataSource
     */
    @Bean
    @ConfigurationProperties("spring.datasource.druid")
    public DataSource dataSourceOne(){
        return DruidDataSourceBuilder.create().build();
    }

}


DruidStatFilter类
package com.soneer.fp.datasource;

import com.alibaba.druid.support.http.WebStatFilter;

import javax.servlet.annotation.WebFilter;
import javax.servlet.annotation.WebInitParam;

/**
 * Servlet Filter implementation class DruidFilter
 */
@WebFilter(
        filterName="druidWebStatFilter",
        urlPatterns= {"/*"},
        initParams= {
                @WebInitParam(name="exclusions",value="*.js,*.jpg,*.png,*.gif,*.ico,*.css,/druid/*")//配置本过滤器放行的请求后缀
        }
)
public class DruidStatFilter extends WebStatFilter {
}

DruidStatViewServlet 类

package com.soneer.fp.datasource;

import com.alibaba.druid.support.http.StatViewServlet;
import javax.servlet.Servlet;
import javax.servlet.annotation.WebInitParam;
import javax.servlet.annotation.WebServlet;

/**
 * Servlet implementation class DruidStateViewServlet
 */
@WebServlet(
        urlPatterns= {"/druid/*"},
        initParams= {
                @WebInitParam(name="allow",value="127.0.0.1"),
                @WebInitParam(name="loginUsername",value="root"),
                @WebInitParam(name="loginPassword",value="123"),
                @WebInitParam(name="resetEnable",value="true")// 允许HTML页面上的“Reset All”功能

        }
)
public class DruidStatViewServlet extends StatViewServlet implements Servlet {
    private static final long serialVersionUID = 1L;


}


 

LoginUser类

package com.soneer.fp.pojo;

import java.io.Serializable;

public class LoginUser implements Serializable{

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	
	private int userId;
	private String userName;
	private String trueName;
	private String userType;
	private String password;
	private String duty;
	private String unit;
	private String phone;
	private int rolesId;
	private Integer bfId;
	private Integer approles;
	
	public int getUserId() {
		return userId;
	}
	public void setUserId(int userId) {
		this.userId = userId;
	}
	public String getUserName() {
		return userName;
	}
	public void setUserName(String userName) {
		this.userName = userName;
	}
	public String getTrueName() {
		return trueName;
	}
	public void setTrueName(String trueName) {
		this.trueName = trueName;
	}
	public String getUserType() {
		return userType;
	}
	public void setUserType(String userType) {
		this.userType = userType;
	}
	public String getPassword() {
		return password;
	}
	public void setPassword(String password) {
		this.password = password;
	}
	public String getDuty() {
		return duty;
	}
	public void setDuty(String duty) {
		this.duty = duty;
	}
	public String getUnit() {
		return unit;
	}
	public void setUnit(String unit) {
		this.unit = unit;
	}
	public String getPhone() {
		return phone;
	}
	public void setPhone(String phone) {
		this.phone = phone;
	}
	public int getRolesId() {
		return rolesId;
	}
	public void setRolesId(int rolesId) {
		this.rolesId = rolesId;
	}
	public Integer getBfId() {
		return bfId;
	}
	public void setBfId(Integer bfId) {
		this.bfId = bfId;
	}
	public Integer getApproles() {
		return approles;
	}
	public void setApproles(Integer approles) {
		this.approles = approles;
	}
	
	@Override
	public String toString() {
		return "LoginUser [userId=" + userId + ", userName=" + userName + ", trueName=" + trueName + ", userType="
				+ userType + ", password=" + password + ", duty=" + duty + ", unit=" + unit + ", phone=" + phone
				+ ", rolesId=" + rolesId + ", bfId=" + bfId + ", approles=" + approles + "]";
	}
	
}

 

ShiroConfig类

package com.soneer.fp.shiro;

import java.util.LinkedHashMap;
import java.util.Map;

import org.apache.shiro.mgt.SecurityManager;
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 javax.annotation.Resource;

/**
 * Shiro配置类
 * 1.配置ShiroFilterFactory 2.配置SecurityManager
 * @author zhengkai
 *
 */
@Configuration
public class ShiroConfig {
    @Resource
    private UserRealm userRealm;
    /**
     * 配置shiro过滤器
     * @author zhengkai
     */
    @Bean("shiroFilter")
    public ShiroFilterFactoryBean shiroFilter(@Qualifier("securityManager")SecurityManager securityManager) {
        //1.定义shiroFactoryBean
        ShiroFilterFactoryBean shiroFilterFactoryBean=new ShiroFilterFactoryBean();
        //2.设置securityManager
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        //3.LinkedHashMap是有序的,进行顺序拦截器配置
        Map<String,String> filterChainMap = new LinkedHashMap<String,String>();
        //4.配置logout过滤器
        filterChainMap.put("/logout", "logout");
        //登陆和主页不需要认证
        filterChainMap.put("/login","anon");
        filterChainMap.put("/","anon");
        filterChainMap.put("/checkLogin","anon");
        filterChainMap.put("/css/**", "anon"); //匿名访问静态资源
        filterChainMap.put("/js/**", "anon"); //匿名访问静态资源
        filterChainMap.put("/fonts/**", "anon"); //匿名访问静态资源
        filterChainMap.put("/images/**", "anon"); //匿名访问静态资源
        filterChainMap.put("/lib/**", "anon"); //匿名访问静态资源


        //5.所有url必须通过认证才可以访问
        filterChainMap.put("/**","authc");

        //6.设置默认登录的url
        shiroFilterFactoryBean.setLoginUrl("/login");
        //7.设置成功之后要跳转的链接
        shiroFilterFactoryBean.setSuccessUrl("/index");
        //8.设置未授权界面
        shiroFilterFactoryBean.setUnauthorizedUrl("/403");
        //9.设置shiroFilterFactoryBean的FilterChainDefinitionMap
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainMap);
        return shiroFilterFactoryBean;
    }
    /**
     * 配置安全管理器
     * @author zhengkai
     */
    @Bean
    public SecurityManager securityManager() {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        //设置realm.
        securityManager.setRealm(userRealm);//将自定义的realm注入到securityManager中
        return securityManager;
    }

}

自定义UserRealm类

package com.soneer.fp.shiro;


import javax.annotation.Resource;

import com.soneer.fp.pojo.LoginUser;
import com.soneer.fp.service.impl.LoginUserBizImpl;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UsernamePasswordToken;
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.jboss.logging.Logger;
import org.springframework.stereotype.Component;

@Component
public class UserRealm extends AuthorizingRealm {
	
	private Logger logger=Logger.getLogger(getClass());

	@Resource
	private LoginUserBizImpl userBiz;
	
	 //用户角色和权限授权
	@Override
	protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection Principal){
		String userName=(String)Principal.getPrimaryPrincipal();
		SimpleAuthorizationInfo authorizationInfo=new SimpleAuthorizationInfo();
		try {
			authorizationInfo.setRoles(userBiz.chkRoleName(userName));
			authorizationInfo.setStringPermissions(userBiz.chkPermissions(userName));
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		System.out.println(authorizationInfo);
		return authorizationInfo;
	}
   
	//用户身份登陆认证
	@Override
	protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authcToken) throws AuthenticationException {
		
		UsernamePasswordToken token = (UsernamePasswordToken) authcToken;
		String userName = token.getUsername();
		logger.info("doGetAuthenticationInfo username--------->:"+userName);
		
		
		String username=(String)token.getPrincipal();
		//String username=userName;
		try {
			LoginUser user=userBiz.chkByUserName(username);
			if(user!=null){
				//System.out.println(user.getUserName()+"===="+user.getPassword());
				AuthenticationInfo authcInfo=new SimpleAuthenticationInfo(user.getUserName(),user.getPassword(),"userRealm");
				return authcInfo;
			}else {
				return null;
			}
			
		} catch (Exception e) {
			System.out.println("身份认证失败");
		}
		return null;
	}

}

可能有的人注意到了上面注解@Component  这里是要将UserRealm这个类注入到securityManager中才能起到作用,这里我一开始在上面的ShiroCongfig类中没有将自定义UserRealm这个类注入进去,导致报错No realms have been configured! One or more realms must be 。。。。

 

LoginUserMapper


import com.soneer.fp.pojo.LoginUser;
import org.apache.ibatis.annotations.Param;


public interface LoginUserMapper {
	/**
	 * 
	 * @param username
	 * @param password
	 * @return
	 */
	LoginUser loginByuser(@Param("username") String username, @Param("password") String password);
   /**
    * 
    * @param username
    * @return
    */
	LoginUser chkByUserName(@Param("username") String username);
   /**
    * 
    * @return
    * @param username
    */
   Set<String> chkRoleName(@Param("username") String username);
   /**
    * 
    * @param username
    * @return
    * @throws Exception
    */
   Set<String> chkPermissions(String username);

}

LoginUserMapper.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.soneer.fp.mapper.LoginUserMapper">

	<resultMap type="com.soneer.fp.pojo.LoginUser" id="loginUser">
        <!--这里这个字段和表中字段不一样,所以只做了这一个配置>
		<result column="bf_id" property="bfId"/>
	</resultMap>

	<select id="loginByuser" resultType="com.soneer.fp.pojo.LoginUser">
		select * from loginusers
		where username=#{username} and password=#{password}
	</select>

	<select id="chkByUserName" resultType="com.soneer.fp.pojo.LoginUser">
		select * from loginusers
		where username=#{username}
	</select>

	<select id="chkRoleName" parameterType="String" resultType="String">
		SELECT r.roleName from loginusers u, loginroles r WHERE u.rolesId=r.rolesId and u.username=#{username}
	</select>

	<select id="chkPermissions" parameterType="String" resultType="String">
		SELECT p.permissionName from loginusers u, loginroles r, permission p
		WHERE u.rolesId=r.rolesId and p.rolesId=r.rolesId and
		userName=#{username}
	</select>
</mapper>

Controller类

package com.soneer.fp.controller;

import com.soneer.fp.pojo.LoginUser;
import com.soneer.fp.service.impl.LoginUserBizImpl;
import com.soneer.fp.util.Md5Util;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.jboss.logging.Logger;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.Date;

@Controller
public class AuthorController {
    private Logger logger = Logger.getLogger(getClass());

    @Resource
    private LoginUserBizImpl userBiz;
    

    @ResponseBody
    @PostMapping(value = "/checkLogin")
    public String chkUser(LoginUser user, HttpServletRequest request, Model model) throws Exception {
//        System.out.println("进入身份认证");
//        System.out.println(user.getUserName() + "==" + user.getPassword());
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken token = new UsernamePasswordToken(user.getUserName(), Md5Util.md5(user.getPassword()));
        try {
            subject.login(token);// 会跳到我们自定义的realm中
            subject.getSession().setAttribute("user", user);
            logger.info(user.getUserName() + "登陆了系统" + new Date());

            // userBiz
            LoginUser loginUser = userBiz.chkByUserName(user.getUserName());
            System.out.println(loginUser.getPassword());
            /*
             * String truename=""; if(shiroUser != null){ truename =
             * shiroUser.getTrueName(); }
             */
            logger.info(loginUser.toString());
            /*
             * model.addAttribute("truename", loginUser.getTrueName());
             * model.addAttribute("userName", loginUser.getUserName());
             * model.addAttribute("phone", loginUser.getPhone());
             * model.addAttribute("rolesId", loginUser.getRolesId());
             */

            return "true";

        } catch (AuthenticationException a) {
            System.out.println(a.getMessage());
            logger.info(user.getUserName() + "用户登陆过系统,身份认证失败!" + new Date());
            return "false";
        }



    }

}

这里有Service那一层没贴出来,都知道怎么玩,这个就不细说了。

用postman测试没问题

以上便是记录一下自己整个shiro的过程,只是简单的实现。更多的功能还需深入学习。也是第一次写博客,写的不好,请大家见谅哈~

可能遇到的坑:

     1,shiro在配置过滤器的过程中可能会出现把静态资源也会拦截掉,解决办法是在ShiroConfig类中将filterChainMap.put("/static/**", "anon");就可以了。但是页面还是访问不到。原因是springboot默认会将static目录做为classes根目录发布到web服务器,所以要filterChainMap.put("/xx/**", "anon"); 这里xx设置成你相应的文件夹就可以了。

     2,上面也已经说到,UserRealm这个自定义类需要注入到securityManager中才能起到作用,,否则导致报错:No realms have been configured! One or more realms must be 。。。。

 

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值