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