SpringBoot与Shiro整合
一、数据库设计
这里主要涉及到五张表:用户表,角色表(用户所拥有的角色),权限表(角色所涉及到的权限),用户-角色表(用户和角色是多对多的),角色-权限表(角色和权限是多对多的).表结构建立的sql语句如下:
CREATE TABLE `module` (
`mid` int(11) NOT NULL AUTO_INCREMENT,
`mname` varchar(255) DEFAULT NULL,
PRIMARY KEY (`mid`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8; -- ----------------------------
-- Records of module
-- ----------------------------
INSERT INTO `module` VALUES ('', 'add');
INSERT INTO `module` VALUES ('', 'delete');
INSERT INTO `module` VALUES ('', 'query');
INSERT INTO `module` VALUES ('', 'update'); -- ----------------------------
-- Table structure for module_role
-- ----------------------------
DROP TABLE IF EXISTS `module_role`;
CREATE TABLE `module_role` (
`rid` int(11) DEFAULT NULL,
`mid` int(11) DEFAULT NULL,
KEY `rid` (`rid`),
KEY `mid` (`mid`),
CONSTRAINT `mid` FOREIGN KEY (`mid`) REFERENCES `module` (`mid`),
CONSTRAINT `rid` FOREIGN KEY (`rid`) REFERENCES `role` (`rid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- ----------------------------
-- Records of module_role
-- ----------------------------
INSERT INTO `module_role` VALUES ('', '');
INSERT INTO `module_role` VALUES ('', '');
INSERT INTO `module_role` VALUES ('', '');
INSERT INTO `module_role` VALUES ('', '');
INSERT INTO `module_role` VALUES ('', '');
INSERT INTO `module_role` VALUES ('', ''); -- ----------------------------
-- Table structure for role
-- ----------------------------
DROP TABLE IF EXISTS `role`;
CREATE TABLE `role` (
`rid` int(11) NOT NULL AUTO_INCREMENT,
`rname` varchar(255) DEFAULT NULL,
PRIMARY KEY (`rid`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8; -- ----------------------------
-- Records of role
-- ----------------------------
INSERT INTO `role` VALUES ('', 'admin');
INSERT INTO `role` VALUES ('', 'customer'); -- ----------------------------
-- Table structure for user
-- ----------------------------
DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
`uid` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(255) DEFAULT NULL,
`password` varchar(255) DEFAULT NULL,
PRIMARY KEY (`uid`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8; -- ----------------------------
-- Records of user
-- ----------------------------
INSERT INTO `user` VALUES ('', 'wulifu', '');
INSERT INTO `user` VALUES ('', 'root', ''); -- ----------------------------
-- Table structure for user_role
-- ----------------------------
DROP TABLE IF EXISTS `user_role`;
CREATE TABLE `user_role` (
`uid` int(11) DEFAULT NULL,
`rid` int(11) DEFAULT NULL,
KEY `u_fk` (`uid`),
KEY `r_fk` (`rid`),
CONSTRAINT `r_fk` FOREIGN KEY (`rid`) REFERENCES `role` (`rid`),
CONSTRAINT `u_fk` FOREIGN KEY (`uid`) REFERENCES `user` (`uid`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- ----------------------------
-- Records of user_role
-- ----------------------------
INSERT INTO `user_role` VALUES ('', '');
INSERT INTO `user_role` VALUES ('', '');
二、功能要求
admin要求只有具有admin角色的用户才能访问,update需要有update权限的用户才能访问,login、loginUser都不做拦截。
预期目标:
wulifu是有admin角色和所有权限,所以用wulifu登录后,可以访问update和admin,但是不能访问guest;而root是customer角色,只有add和query权限,所以不能访问admin和update。
三、添加依赖,配置文件
1、springboot项目,项目结构如下:
2、添加依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.6.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-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.20</version>
</dependency>
<!--常用的工具包-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.4</version>
</dependency>
<!--spring的上下文工具包-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>4.1.7.RELEASE</version>
</dependency>
<!--对jsp的处理-->
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
</dependency>
</dependencies>
3、application.yml
server:
port: 8010
tomcat.uri-encoding: UTF-8 spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/shiro?characterEncoding=UTF-8
username: root
password: 123456
mvc:
view:
prefix: /WEB-INF/jsp/
suffix: .jsp
mybatis:
mapper-locations: mappers/*.xml
# mapper.xml中的resultType中经常会用到一些自定义POJO,你可以用完全限定名来指定这些POJO的引用,例如
# <select id="getUsers" resultType="com.majing.learning.mybatis.entity.User">,
# 又或者你可以通过在application.yml中指定POJO扫描包来让mybatis自动扫描到自定义POJO,如下:
# mybatis:type-aliases-package: com.majing.learning.mybatis.entity
type-aliases-package: com.example.springbootshiro.pojo
三、项目设计
1、pojo层
User.java
public class User implements Serializable {
private Integer uid;
private String username;
private String password;
private Set<Role> roles = new HashSet<>();
}
Role.java
public class Role implements Serializable {
private Integer rid;
private String rname;
private Set<Module> modules = new HashSet<>();
}
Module.java
public class Module implements Serializable {
private Integer mid;
private String mname;
}
2、dao层
UserMapper.java
import com.example.springbootshiro.pojo.User;
import org.springframework.stereotype.Repository; @Repository
public interface UserMapper {
User findByUserName(String username);
}
UserMapper.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.example.springbootshiro.dao.UserMapper"> <resultMap id="userMap" type="com.example.springbootshiro.pojo.User">
<id property="uid" column="uid"/>
<result property="username" column="username"/>
<result property="password" column="password"/>
<collection property="roles" ofType="com.example.springbootshiro.pojo.Role">
<id property="rid" column="rid"/>
<result property="rname" column="rname"/>
<collection property="modules" ofType="com.example.springbootshiro.pojo.Module">
<id property="mid" column="mid"/>
<result property="mname" column="mname"/>
</collection>
</collection>
</resultMap> <select id="findByUserName" parameterType="string" resultMap="userMap">
SELECT u.*,r.*,m.* FROM user u
inner join user_role ur on ur.uid=u.uid
inner join role r on r.rid=ur.rid
inner join module_role mr on mr.rid=r.rid
inner join module m on mr.mid=m.mid
WHERE username=#{username};
</select> </mapper>
图中红框内可能会报错,但是依然可以正常运行。
3、service层
IUserService.Interface
public interface IUserService {
User findByUserName(String username);
}
UserServiceImpl.java
@Service("iUserService")
public class UserServiceImpl implements IUserService {
@Autowired
private UserMapper userMapper; @Override
public User findByUserName(String username) {
return userMapper.findByUserName(username);
}
}
4、controller层
TestController.java
@Controller
public class TestController { @RequestMapping("/login")
public String login(){
return "login";
} @RequestMapping("/index")
public String index(){
return "index";
} @RequestMapping("/unauthorized")
public String unauthorized() {
return "unauthorized";
} /**
* 拥有admin角色的人才能访问
* @return
*/
@RequestMapping("/admin")
public String admin(){
return "admin";
} /**
* 拥有update权限的人才能访问
* @return
*/
@RequestMapping("/update")
public String update(){
return "update";
} @RequestMapping("/logout")
public String logout(){
Subject subject = SecurityUtils.getSubject();//取出当前验证主体
if(subject != null){
subject.logout(); //执行一次logout的操作,将session全部清空
}
return "login";
} /**
* 整个form表单的验证流程:
* 将登陆的用户/密码传入UsernamePasswordToken,当调用subject.login(token)开始,
* 调用Relam的doGetAuthenticationInfo方法,开始密码验证
* 此时这个时候执行我们自己编写的CredentialMatcher(密码匹配器),
* 执行doCredentialsMatch方法,具体的密码比较实现在这实现
*/
@RequestMapping(value = "/loginUser")
public String loginUser(@RequestParam("username") String username,
@RequestParam("password") String password,
HttpSession session) {
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
Subject subject = SecurityUtils.getSubject();
try {
subject.login(token); //登陆成功的话,放到session中
User user = (User) subject.getPrincipal();
session.setAttribute("user", user);
return "index";
}catch (Exception e) {
return "login";
}
}
}
5、jsp页面
login.jsp(登录页面)
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>login</title>
</head>
<body>
<form action="/loginUser" method="post">
<input type="text" name="username"> <br>
<input type="password" name="password"> <br>
<input type="submit" value="提交">
</form>
</body>
</html>
index.jsp(登录成功后跳转的页面)
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>登录</title>
</head>
<body>
<h1> 欢迎登录, ${user.username} </h1>
</body>
</html>
unauthorized.jsp (无权访问跳转的页面)
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
Unauthorized!
</body>
</html>
admin.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>admin</title>
</head>
<body>
admin!
</body>
</html>
update.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>update</title>
</head>
<body>
update!
</body>
</html>
四、配置Shiro
1、核心配置类:ShiroConfiguration.java
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import java.util.LinkedHashMap; @Configuration
public class ShiroConfiguration { /**
* @Qualifier("XXX") Spring的Bean注入配置注解,该注解指定注入的Bean的名称,
* Spring框架使用byName方式寻找合格的bean,这样就消除了byType方式产生的歧义。
*/
@Bean("shiroFilter")
public ShiroFilterFactoryBean shiroFilter(@Qualifier("securityManager") SecurityManager manager) {
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
System.out.println(bean);
bean.setSecurityManager(manager); bean.setLoginUrl("/login"); //提供登录到url
bean.setSuccessUrl("/index"); //提供登陆成功的url
bean.setUnauthorizedUrl("/unauthorized"); /**
* 可以看DefaultFilter,这是一个枚举类,定义了很多的拦截器authc,anon等分别有对应的拦截器
*/
//配置访问权限
LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
filterChainDefinitionMap.put("/index", "authc"); //代表着前面的url路径,用后面指定的拦截器进行拦截
filterChainDefinitionMap.put("/login", "anon");
filterChainDefinitionMap.put("/loginUser", "anon");
filterChainDefinitionMap.put("/admin", "roles[admin]"); //admin的url,要用角色是admin的才可以登录,对应的拦截器是RolesAuthorizationFilter
filterChainDefinitionMap.put("/update", "perms[update]"); //拥有update权限的用户才有资格去访问
//filterChainDefinitionMap.put("/druid/**", "anon"); //所有的druid请求,不需要拦截,anon对应的拦截器不会进行拦截
filterChainDefinitionMap.put("/**", "user"); //所有的路径都拦截,被UserFilter拦截,这里会判断用户有没有登陆
bean.setFilterChainDefinitionMap(filterChainDefinitionMap);//设置一个拦截器链 return bean;
} /**
* 定义安全管理器securityManager,注入自定义的realm
* @param authRealm
* @return
*/
@Bean("securityManager")
public SecurityManager securityManager(@Qualifier("authRealm") AuthRealm authRealm) {
//这个DefaultWebSecurityManager构造函数,会对Subject,realm等进行基本的参数注入
DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
manager.setRealm(authRealm);//往SecurityManager中注入Realm,代替原本的默认配置
return manager;
} //自定义的Realm
@Bean("authRealm")
//@DependsOn("lifecycleBeanPostProcessor") //可选
public AuthRealm authRealm(@Qualifier("credentialsMatcher") CredentialMatcher matcher) {
AuthRealm authRealm = new AuthRealm();
//这边可以选择是否将认证的缓存到内存中,现在有了这句代码就将认证信息缓存的内存中了
//authRealm.setCacheManager(new MemoryConstrainedCacheManager());
//最简单的情况就是明文直接匹配,然后就是加密匹配,这里的匹配工作则就是交给CredentialsMatcher来完成
authRealm.setCredentialsMatcher(matcher);
return authRealm;
} /**
* Realm在验证用户身份的时候,要进行密码匹配
* 最简单的情况就是明文直接匹配,然后就是加密匹配,这里的匹配工作则就是交给CredentialsMatcher来完成
* 支持任意数量的方案,包括纯文本比较、散列比较和其他方法。除非该方法重写,否则默认值为
* @return
*/
@Bean("credentialsMatcher")
public CredentialMatcher credentialsMatcher() {
CredentialMatcher credentialsMatcher = new CredentialMatcher();
return credentialsMatcher;
} /**
* 配置shiro跟spring的关联
* 以下AuthorizationAttributeSourceAdvisor,DefaultAdvisorAutoProxyCreator两个类是为了支持shiro注解
* @param securityManager
* @return
*/
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(@Qualifier("securityManager") SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
advisor.setSecurityManager(securityManager);
return advisor;
} /**
* Spring的一个bean , 由Advisor决定对哪些类的方法进行AOP代理
* @return
*/
@Bean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator creator = new DefaultAdvisorAutoProxyCreator();
creator.setProxyTargetClass(true);
return creator;
} /**
* lifecycleBeanPostProcessor是负责生命周期的 , 初始化和销毁的类
* (可选)
* @return
*/
@Bean("lifecycleBeanPostProcessor")
public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
} }
这个SecurityManager的导包是org.apache.shiro.mgt.SecurityManager,不要导了java的包。
2、自定义域:AuthRealm.java
编写AuthRealm完成根据用户名去数据库的查询,并且将用户信息放入shiro中,供核心配置类:ShiroConfiguration调用。有认证与授权的方法。
import com.example.springbootshiro.pojo.Module;
import com.example.springbootshiro.pojo.Role;
import com.example.springbootshiro.pojo.User;
import com.example.springbootshiro.service.IUserService;
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.springframework.beans.factory.annotation.Autowired; import java.util.*; //AuthenticatingRealm是抽象类,用于认证授权
public class AuthRealm extends AuthorizingRealm { @Autowired
private IUserService iUserService; /**
* 用户授权
* @param principals
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
//获取前端输入的用户信息,封装为User对象
User userweb = (User) principals.getPrimaryPrincipal();
//获取前端输入的用户名
String username = userweb.getUsername();
//根据前端输入的用户名查询数据库中对应的记录
User user = iUserService.findByUserName(username);
if(user != null){
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//因为addRoles和addStringPermissions方法需要的参数类型是Collection
//所以先创建两个collection集合
Collection<String> rolesCollection = new HashSet<String>();
Collection<String> perStringCollection = new HashSet<String>();
//获取user的Role的set集合
Set<Role> roles = user.getRoles();
//遍历集合
for (Role role : roles){
//将每一个role的name装进collection集合
rolesCollection.add(role.getRname());
//获取每一个Role的permission的set集合
Set<Module> permissionSet = role.getModules();
//遍历集合
for (Module permission : permissionSet){
//将每一个permission的name装进collection集合
perStringCollection.add(permission.getMname());
System.out.println(permission.getMname());
}
//为用户授权
info.addStringPermissions(perStringCollection);
}
//为用户授予角色
info.addRoles(rolesCollection);
return info; }
return null;
} /**
* 用于认证登录,认证接口实现方法,该方法的回调一般是通过subject.login(token)方法来实现的
* AuthenticationToken 用于收集用户提交的身份(如用户名)及凭据(如密码):
* AuthenticationInfo是包含了用户根据username返回的数据信息,用于在匹马比较的时候进行相互比较
*
* shiro的核心是java servlet规范中的filter,通过配置拦截器,使用拦截器链来拦截请求,如果允许访问,则通过。
* 通常情况下,系统的登录、退出会配置拦截器。登录的时候,调用subject.login(token),token是用户验证信息,
* 这个时候会在Realm中doGetAuthenticationInfo方法中进行认证。这个时候会把用户提交的验证信息与数据库中存储的认证信息,将所有的数据拿到,在匹配器中进行比较
* 这边是我们自己实现的CredentialMatcher类的doCredentialsMatch方法,返回true则一致,false则登陆失败
* 退出的时候,调用subject.logout(),会清除回话信息
* @param token
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
//token携带了用户信息
UsernamePasswordToken usernamePasswordToken = (UsernamePasswordToken) token;
//获取前端输入的用户名
String username = usernamePasswordToken.getUsername();
//根据用户名查询数据库中对应的记录
User user = iUserService.findByUserName(username); /*//当前realm对象的name
String realmName = getName();
//盐值
ByteSource credentialsSalt = ByteSource.Util.bytes(user.getUsername());
//封装用户信息,构建AuthenticationInfo对象并返回
AuthenticationInfo authcInfo = new SimpleAuthenticationInfo(user, user.getPassword(),
credentialsSalt, realmName);*/
return new SimpleAuthenticationInfo(user,user.getPassword(),this.getClass().getName());
} }
3、数据库密码为明文时密码匹配类:CredentialMatcher.java
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authc.credential.SimpleCredentialsMatcher; public class CredentialMatcher extends SimpleCredentialsMatcher {
@Override
public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
UsernamePasswordToken utoken=(UsernamePasswordToken) token;
//获得用户输入的密码:(可以采用加盐(salt)的方式去检验)
String inPassword = new String(utoken.getPassword());
//获得数据库中的密码
String dbPassword=(String) info.getCredentials();
//进行密码的比对
return this.equals(inPassword, dbPassword);
}
}
4、数据库为加密加盐时
authRealm引用的密码匹配需要更换。
以下密码匹配直接写在核心配置类:ShiroConfiguration.java中
/**
* 密码校验规则HashedCredentialsMatcher
* 这个类是为了对密码进行编码的 ,
* 防止密码在数据库里明码保存 , 当然在登陆认证的时候 ,
* 这个类也负责对form里输入的密码进行编码
* 处理认证匹配处理器:如果自定义需要实现继承HashedCredentialsMatcher
*/
@Bean("hashedCredentialsMatcher")
public HashedCredentialsMatcher hashedCredentialsMatcher() {
HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
//指定加密方式为MD5
credentialsMatcher.setHashAlgorithmName("MD5");
//加密次数
credentialsMatcher.setHashIterations(1024);
credentialsMatcher.setStoredCredentialsHexEncoded(true);
return credentialsMatcher;
}
附上明文转密文的代码:
public static void main(String[] args) {
String hashAlgorithName = "MD5";
String password = "登录时输入的密码";
int hashIterations = 1;//加密次数
ByteSource credentialsSalt = ByteSource.Util.bytes("登录时输入的用户名");
Object obj = new SimpleHash(hashAlgorithName, password, credentialsSalt, hashIterations);
System.out.println(obj);
}
SpringBoot与Shiro整合的更多相关文章
- springboot 与 shiro 整合 (简洁版)
前言: 网上有很多springboot 与 shiro 整合的资料,有些确实写得很好, 对学习shiro和springboot 都有很大的帮助. 有些朋友比较省事, 直接转发或者复制粘贴.但是没有经过 ...
- SpringBoot与Shiro整合权限管理实战
SpringBoot与Shiro整合权限管理实战 作者 : Stanley 罗昊 [转载请注明出处和署名,谢谢!] *观看本文章需要有一定SpringBoot整合经验* Shiro框架简介 Apach ...
- springboot,vue,shiro整合 关于登录认证功能
首先是session问题 传统session认证 http协议是一种无状态协议,即浏览器发送请求到服务器,服务器是不知道这个请求是哪个用户发来的.为了让服务器知道请求是哪个用户发来的,需要让用户提供用 ...
- SpringBoot:Shiro 整合 Redis
前言 前段时间做了一个图床的小项目,安全框架使用的是Shiro.为了使用户7x24小时访问,决定把项目由单机升级为集群部署架构.但是安全框架shiro只有单机存储的SessionDao,尽管Shrio ...
- 在 springboot 中如何整合 shiro 应用 ?
Shiro是Apache下的一个开源项目,我们称之为Apache Shiro. 它是一个很易用与Java项目的的安全框架,提供了认证.授权.加密.会话管理,与spring Security 一样都是 ...
- Shiro整合springboot,freemaker,redis(含权限系统完整源码)
区块链技术联盟 2018-02-08 17:06:40 目录 一.导语 二.shiro功能介绍 三.shiro详解 四.shiro实战案例分享 五.系统配置 六.其他 一.导语 今天推荐给大家一个非常 ...
- 教你 Shiro 整合 SpringBoot,避开各种坑
教你 Shiro 整合 SpringBoot,避开各种坑-----https://www.cnblogs.com/HowieYuan/p/9259638.html
- Springboot + shiro 整合之Url拦截设置(转)
shiro 整合到springboot 还是比较简单的,只需要新建一个spring-shiro.xml的配置文件: <span style="font-size:14px;" ...
- [03] SpringBoot+MyBatis+Shiro搭建杂谈
0.写在前面的话 一直想能仿公司框架的形式,着手做一个简单的脚手架,一来是带着目标性能更好地学习,接触新的技术,另外自己如果有什么想要实现的简单需求,就可以进行快速开发,主要还是希望能在权限上有所控制 ...
随机推荐
- AcWing 9. 分组背包问题
#include <iostream> #include <algorithm> using namespace std; ; int n, m; int v[N][N], w ...
- selenium-JavaScript的处理
JavaScript的处理 在自动化过程中,遇到js处理的元素,需要使用js语言对元素进行操作,例如,滑动到浏览器的底部或者顶部,时间控件的处理,元素可见不可见以及富文本的处理等,都需要js语言的支持 ...
- Linux新建用户,切换后只显示$问题
1,执行以下命令创建一个新的用户 useradd -d /home/sam -m sam -s /bin/sh -g group -G adm,root 这个命令中指定了这个用户登录的shell 是/ ...
- ubuntu16.04无法打开终端
最近将自带的python3.5更改为默认的python3.6,所以就出现了终端打不开的情况,以下是我的解决办法: 首先,按ctrl+alt+F1进入命令行模式,也就是无图形截面,这时候会让你输入用户名 ...
- 第十八篇 Linux环境下常用软件安装和使用指南
提醒:如果之后要安装virtualenvwrapper的话,可以直接跳到安装virtualenvwrapper的方法,而不需要先安装好virtualenv 安装virtualenv和生 ...
- 生成树计数 UVA 10766
//本题题意:首先每个点之间都可达,然后m列举出不可达的,求出最多的生成树方案: //k这个变量是没用的. //公式:ans矩阵=度矩阵-建边矩阵: //度矩阵是当i==j时的,建边矩阵于平时定义可达 ...
- vuex中怎么直接获取state中的值,以及computed的使用注意
1,直接用$store对象获取store对象,再进一步获取state属性..... 2, 3,computed computed是计算属性,他不可以直把值直接存入data中,因此不能像data一样直接 ...
- 题解【POJ2955】Brackets
Description We give the following inductive definition of a "regular brackets" sequence: t ...
- 【资源分享】Undertale(传说之下)简体中文精品整合包
*----------------------------------------------[下载区]----------------------------------------------* ...
- java.lang.NoClassDefFoundError: org/apache/commons/logging/LogFactory解决方法
解决方法 第一种方法:导入commons-logging.jar包 第二种方法,如果用的是maven项目,则直接在pom.xml中加入commons-logging依赖包,如下: <depend ...