Spring Boot(八)——Shiro+FreeMarker
Shiro简介
Apache Shiro是一个功能强大、灵活的,开源的安全框架。它可以干净利落地处理身份验证、授权、企业会话管理和加密。
Shiro能做什么呢
- 用户访问权限控制,比如:判断用户是否分配了一定的安全角色,判断用户是否被授予完成某个操作的权限;
- 在非 web 或 EJB 容器的环境下可以任意使用Session API;
- 可以响应认证、访问控制,或者 Session 生命周期中发生的事件;
- 可将一个或以上用户安全数据源数据组合成一个复合的用户 “view”(视图);
- 支持单点登录(SSO)功能;
- 支持提供“Remember Me”服务,获取用户关联信息而无需登录;
- …等等——都集成到一个有凝聚力的易于使用的API。
Apache Shiro Features 特性
Apache Shiro是一个全面的、蕴含丰富功能的安全框架。下图为描述Shiro功能的框架图:
Authentication(认证), Authorization(授权), Session Management(会话管理), Cryptography(加密)被 Shiro 框架的开发团队称之为应用安全的四大基石。
Authentication:用户身份识别,通常被称为用户“登录”
Authorization:访问控制。比如某个用户是否具有某个操作的使用权限。
Session Management:特定于用户的会话管理,甚至在非web 或 EJB 应用程序。
Cryptography:在对数据源使用加密算法加密的同时,保证易于使用。
还有其他的功能来支持和加强这些不同应用环境下安全领域的关注点。特别是对以下的功能支持:
Web支持:Shiro 提供的 web 支持 api ,可以很轻松的保护 web 应用程序的安全。
缓存:缓存是 Apache Shiro 保证安全操作快速、高效的重要手段。
并发:Apache Shiro 支持多线程应用程序的并发特性。
测试:支持单元测试和集成测试,确保代码和预想的一样安全。
“Run As”:这个功能允许用户假设另一个用户的身份(在许可的前提下)。
“Remember Me”:跨 session 记录用户的身份,只有在强制需要时才需要登录。
注意: Shiro不会去维护用户、维护权限,这些需要我们自己去设计/提供,然后通过相应的接口注入给Shiro
FreeMarker
FreeMarker简介
FreeMarker是一款模板引擎: 即一种基于模板和要改变的数据, 并用来生成输出文本(HTML网页、电子邮件、配置文件、源代码等)的通用工具。 它不是面向最终用户的,而是一个Java类库,是一款程序员可以嵌入他们所开发产品的组件。
FreeMarker是免费的,基于Apache许可证2.0版本发布。其模板编写为FreeMarker Template Language(FTL),属于简单、专用的语言。需要准备数据在真实编程语言中来显示,比如数据库查询和业务运算, 之后模板显示已经准备好的数据。在模板中,主要用于如何展现数据, 而在模板之外注意于要展示什么数据 [1] 。
jsp、freemarker、velocity区别
在java领域,表现层技术主要有三种:jsp、freemarker、velocity。
jsp用的最多最广泛
优点:
1、功能强大,可以写java代码
2、支持jsp标签(jsp tag)
3、支持表达式语言(el)
4、官方标准,用户群广,丰富的第三方jsp标签库
5、性能良好。jsp编译成class文件执行,有很好的性能表现
缺点:
jsp没有明显缺点,非要挑点骨头那就是,由于可以编写java代码,如使用不当容易破坏mvc结构。
velocity是较早出现的用于代替jsp的模板语言
优点:
1、不能编写java代码,可以实现严格的mvc分离
2、性能良好,据说比jsp性能还要好些
3、使用表达式语言,据说jsp的表达式语言就是学velocity的
缺点:
1、不是官方标准
2、用户群体和第三方标签库没有jsp多。
3、对jsp标签支持不够好
freemarker
优点:
1、不能编写java代码,可以实现严格的mvc分离
2、性能非常不错
3、对jsp标签支持良好
4、内置大量常用功能,使用非常方便
5、宏定义(类似jsp标签)非常方便
6、使用表达式语言
缺点:
1、不是官方标准
2、用户群体和第三方标签库没有jsp多
选择freemarker的原因:
1、性能。velocity应该是最好的,其次是jsp,普通的页面freemarker性能最差(虽然只是几毫秒到十几毫秒的差距)。但是在复杂页面上(包含大量判断、日期金额格式化)的页面上,freemarker的性能比使用tag和el的jsp好。
2、宏定义比jsp tag方便
3、内置大量常用功能。比如html过滤,日期金额格式化等等,使用非常方便
4、支持jsp标签
5、可以实现严格的mvc分离
使用配置
配置pom.xml
<!--shiro--><dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-core</artifactId><version>${shiro.version}</version></dependency><dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-spring</artifactId><version>${shiro.version}</version></dependency><dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-web</artifactId><version>${shiro.version}</version></dependency> <!--freemarker--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-freemarker</artifactId></dependency>
配置application.properties
这里只显示shiro和freemarker有关的依赖包,文章最下方的github里有全部的pom.xml
# jdbc_config
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://120.79.226.103:3306/base?characterEncoding=utf8
spring.datasource.username=root
spring.datasource.password=lghs35123# jpa
spring.jpa.show-sql=true
spring.jpa.properties.jadira.usertype.autoRegisterUserTypes=true
#自动创建表,如果表存在就更新
spring.jpa.hibernate.ddl-auto=update# freemarker config
spring.freemarker.allow-request-override=false
spring.freemarker.allow-session-override=false
spring.freemarker.cache=false
spring.freemarker.charset=UTF-8
spring.freemarker.check-template-location=true
spring.freemarker.content-type=text/html
spring.freemarker.enabled=true
spring.freemarker.expose-request-attributes=false
spring.freemarker.expose-session-attributes=false
spring.freemarker.expose-spring-macro-helpers=true
spring.freemarker.prefer-file-system-access=true
spring.freemarker.suffix=.ftl
spring.freemarker.template-loader-path=classpath:/templates/
spring.freemarker.settings.template_update_delay=0
spring.freemarker.settings.default_encoding=UTF-8
spring.freemarker.settings.classic_compatible=true
spring.freemarker.settings.date_format=yyyy-MM-dd
spring.freemarker.settings.time_format=HH:mm:ss
spring.freemarker.settings.datetime_format=yyyy-MM-dd HH:mm:ss
页面
登录页面login.ftl
主页login.ftl
具体内容请到下方的github查看源码
RBAC
RBAC 是基于角色的访问控制(Role-Based Access Control )在 RBAC 中,权限与角色相关联,用户通过成为适当角色的成员而得到这些角色的权限。这就极大地简化了权限的管理。这样管理都是层级相互依赖的,权限赋予给角色,而把角色又赋予用户,这样的权限设计很清楚,管理起来很方便。
采用jpa技术来自动生成基础表格,配置文件中的“spring.jpa.hibernate.ddl-auto=update”要开启,对应的entity如下:
用户信息
/*** <p>* 用户表* </p>** @author LYH* @since 2018-3-28*/
@Entity
@Table(name = "tb_user")
public class User extends BaseEntity {private static final long serialVersionUID = 1L;/*** 用户id*/@Id@GeneratedValue(strategy = GenerationType.AUTO)@Column(name = "id", nullable = false)private Integer id;/*** 账户名*/private String userName;/*** 昵称*/private String nickName;/*** 头像地址*/private String avatar;/*** 用户密码*/private String password;/*** 性别 0 女 1 男*/private Integer sex;/*** 出生日期*/@JSONField(format = "yyyy-MM-dd")private Date birthday;/*** 电话*/private String telephone;/*** 邮箱*/private String email;/*** 住址*/private String address;/*** 逻辑删除状态 0 未删除 1 删除*/private Integer deleteStatus;/*** 是否锁定* <p>* 0 未锁定 1 锁定*/private Integer locked;/*** 用户描述*/private String description;/*** 创建时间*/@JSONField(format = "yyyy-MM-dd HH:mm:ss")private Date createTime;/*** 更新时间*/@JSONField(format = "yyyy-MM-dd HH:mm:ss")private Date updateTime;@ManyToMany(cascade = {CascadeType.REFRESH}, fetch = FetchType.LAZY)@JoinTable(name = "tb_user_role", joinColumns = {@JoinColumn(name = "user_id")}, inverseJoinColumns = {@JoinColumn(name = "role_id")})private java.util.Set<Role> roles;
}
角色信息
/*** 角色表** @author LYH* @since 2018-3-28*/
@Entity
@Table(name = "tb_role")
public class Role extends BaseEntity {/****/private static final long serialVersionUID = -1894163644285296223L;/*** 角色id*/@Id@GeneratedValue(strategy = GenerationType.AUTO)@Column(name = "id", nullable = false)private Integer id;/*** 角色名称*/private String name;/*** 角色key*/private String roleKey;/*** 角色状态,0:正常;1:删除*/private Integer status;/*** 角色描述*/private String description;/*** 创建时间*/@JSONField(format = "yyyy-MM-dd HH:mm:ss")private Date createTime;/*** 更新时间*/@JSONField(format = "yyyy-MM-dd HH:mm:ss")private Date updateTime;@ManyToMany(cascade = {CascadeType.REFRESH}, fetch = FetchType.LAZY)@JoinTable(name = "tb_role_resource", joinColumns = {@JoinColumn(name = "role_id")}, inverseJoinColumns = {@JoinColumn(name = "resource_id")})private java.util.Set<Permission> resources;
}
权限信息
/*** <p>* 权限表* </p>** @author LYH* @since 2018-3-28*/
@Entity
@Table(name = "tb_resource")
public class Permission extends BaseEntity {private static final long serialVersionUID = 1L;/*** 资源id*/@Id@GeneratedValue(strategy = GenerationType.AUTO)@Column(name = "id", nullable = false)private Integer id;/*** 资源名称*/private String name;/*** 资源唯一标识*/private String sourceKey;/*** 资源类型,0:目录;1:菜单;2:按钮*/private Integer type;/*** 资源url*/private String sourceUrl;/*** 层级*/private Integer level;/*** 排序*/private Integer sort;/*** 图标*/private String icon;/*** 是否隐藏* 0显示 1隐藏*/private Integer isHide;/*** 描述*/private String description;/*** 创建时间*/@JSONField(format = "yyyy-MM-dd HH:mm:ss")private Date createTime;/*** 更新时间*/@JSONField(format = "yyyy-MM-dd HH:mm:ss")private Date updateTime;@ManyToOne(fetch = FetchType.LAZY)@JoinColumn(name = "parent_id")private Permission parent;
}
根据以上的代码会自动生成tb_user(用户信息表)、tb_role(角色表)、tb_permission(权限表)、tb_user_role(用户角色表)、tb_role_permission(角色权限表)这五张表,为了方便测试我们给这五张表插入一些初始化数据:
insert into `tb_permission`(`id`,`create_time`,`description`,`icon`,`is_hide`,`level`,`name`,`sort`,`source_key`,`source_url`,`type`,`update_time`,`parent_id`) values (0,'2018-01-10 13:56:51','系统管理',NULL,0,1,'系统管理',1,'system','javascript:void(0);',1,'2018-01-10 13:59:01',NULL),(1,'2018-01-10 13:56:51','用户管理',NULL,0,2,'用户管理',1,'system:user:index','/admin/user/index',1,'2018-01-10 13:59:01',0),(2,'2018-01-10 13:56:51','用户编辑',NULL,0,3,'用户编辑',1,'system:user:edit','/admin/user/edit*',2,'2018-01-10 16:26:42',1),(3,'2018-01-11 16:48:48','用户添加',NULL,0,3,'用户添加',2,'system:user:add','/admin/user/add',2,'2018-01-11 16:49:26',1),(4,'2018-01-11 16:48:48','用户删除',NULL,0,3,'用户删除',3,'system:user:deleteBatch','/admin/user/deleteBatch',2,'2018-01-18 14:11:41',1),(5,'2018-01-11 16:48:48','角色分配',NULL,0,3,'角色分配',4,'system:user:grant','/admin/user/grant/**',2,'2018-01-18 14:11:51',1),(6,'2018-01-12 16:45:10','角色管理',NULL,0,2,'角色管理',2,'system:role:index','/admin/role/index',1,'2018-01-12 16:46:52',0),(7,'2018-01-12 16:47:02','角色编辑',NULL,0,3,'角色编辑',1,'system:role:edit','/admin/role/edit*',2,'2018-01-18 10:24:06',1),(8,'2018-01-12 16:47:23','角色添加',NULL,0,3,'角色添加',2,'system:role:add','/admin/role/add',2,'2018-01-12 16:49:16',6),(9,'2018-01-12 16:47:23','角色删除',NULL,0,3,'角色删除',3,'system:role:deleteBatch','/admin/role/deleteBatch',2,'2018-01-18 14:12:03',6),(10,'2018-01-12 16:47:23','资源分配',NULL,0,3,'资源分配',4,'system:role:grant','/admin/role/grant/**',2,'2018-01-18 14:12:11',6),(11,'2018-01-17 11:21:12','资源管理',NULL,0,2,'资源管理',3,'system:resource:index','/admin/resource/index',1,'2018-01-17 11:21:42',0),(12,'2018-01-17 11:21:52','资源编辑',NULL,0,3,'资源编辑',1,'system:resource:edit','/admin/resource/edit*',2,'2018-01-17 11:22:36',11),(13,'2018-01-17 11:21:54','资源添加',NULL,0,3,'资源添加',2,'system:resource:add','/admin/resource/add',2,'2018-01-17 11:22:39',11),(14,'2018-01-17 11:21:54','资源删除',NULL,0,3,'资源删除',3,'system:resource:deleteBatch','/admin/resource/deleteBatch',2,'2018-01-18 14:12:31',11);
insert into `tb_role`(`id`,`create_time`,`description`,`name`,`role_key`,`status`,`update_time`) values (1,'2018-01-09 17:25:30','超级管理员','administrator','administrator',0,'2018-01-09 17:26:25');
insert into `tb_role_permission`(`role_id`,`resource_id`) values (1,0),(1,1),(1,2),(1,3),(1,4),(1,5),(1,6),(1,7),(1,8),(1,9),(1,10),(1,11),(1,12),(1,13),(1,14);
insert into `tb_user`(`id`,`address`,`avatar`,`birthday`,`create_time`,`delete_status`,`description`,`email`,`locked`,`nick_name`,`password`,`sex`,`telephone`,`update_time`,`user_name`) values (1,'广州','','2018-01-09 17:26:39','2018-01-09 17:26:41',0,'超级管理员','lyh0622@126.com',0,'admin','3931MUEQD1939MQMLM4AISPVNE',1,'13112256276','2018-01-09 17:27:11','admin');
insert into `tb_user_role`(`user_id`,`role_id`) values (1,1);
Shiro 配置
ShiroConfig.class
配置的是ShiroConfig类,Shiro 核心通过Filter来实现,既然是使用 Filter 也是通过URL规则来进行过滤和权限校验,所以我们需要定义一系列关于URL的规则和访问权限。
@Configuration
@Import(ShiroManager.class)
public class ShiroConfig {@Resourceprivate IPermissionService resourceService;@Bean(name = "realm")@DependsOn("lifecycleBeanPostProcessor")@ConditionalOnMissingBeanpublic Realm realm() {return new MyRealm();}/*** 用户授权信息Cache*/@Bean(name = "shiroCacheManager")@ConditionalOnMissingBeanpublic CacheManager cacheManager() {return new MemoryConstrainedCacheManager();}@Bean(name = "securityManager")@ConditionalOnMissingBeanpublic DefaultSecurityManager securityManager() {DefaultSecurityManager sm = new DefaultWebSecurityManager();sm.setCacheManager(cacheManager());return sm;}@Bean(name = "shiroFilter")@DependsOn("securityManager")@ConditionalOnMissingBeanpublic ShiroFilterFactoryBean getShiroFilterFactoryBean(DefaultSecurityManager securityManager, Realm realm) {securityManager.setRealm(realm);ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();shiroFilter.setSecurityManager(securityManager);shiroFilter.setLoginUrl("/admin/login");shiroFilter.setSuccessUrl("/admin/index");shiroFilter.setUnauthorizedUrl("/previlige/no");Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();filterChainDefinitionMap.put("/assets/**", "anon");filterChainDefinitionMap.put("/admin/login", "anon");List<Permission> list = resourceService.findAll();for (Permission resource : list) {filterChainDefinitionMap.put(resource.getSourceUrl(), "perms[" + resource.getSourceKey() + "]");}filterChainDefinitionMap.put("/admin/**", "authc");shiroFilter.setFilterChainDefinitionMap(filterChainDefinitionMap);return shiroFilter;}
}
ShiroManager.class
其中@Import(ShiroManager.class)中的ShiroManager.class是使用shiro的方法注解方式进行权限控制的管理类,具体内容如下:
/*** Shiro Config Manager.* 使用shiro的方法注解方式进行权限控制,也可以在Spring的配置文件中开启shiro的注解支持。* @author LYH*/
public class ShiroManager {/*** 保证实现了Shiro内部lifecycle函数的bean执行*/@Bean(name = "lifecycleBeanPostProcessor")@ConditionalOnMissingBeanpublic LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {return new LifecycleBeanPostProcessor();}/*** 开启注解控制权限的方式,AOP式方法级权限检查** @return*/@Bean(name = "defaultAdvisorAutoProxyCreator")@ConditionalOnMissingBean@DependsOn("lifecycleBeanPostProcessor")public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);return defaultAdvisorAutoProxyCreator;}/*** 开启注解控制权限的方式* 配置shiro框架提供的切面类,用于创建代理对象* @param securityManager* @return*/@Bean@ConditionalOnMissingBeanpublic AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultSecurityManager securityManager) {AuthorizationAttributeSourceAdvisor aasa = new AuthorizationAttributeSourceAdvisor();aasa.setSecurityManager(securityManager);return aasa;}
}
MyRealm.class
在认证、授权内部实现机制中都有提到,最终处理都将交给Real进行处理。因为在Shiro中,最终是通过Realm来获取应用程序中的用户、角色及权限信息的。通常情况下,在Realm中会直接从我们的数据源中获取Shiro需要的验证信息。可以说,Realm是专用于安全框架的DAO。
/*** @author LYH* 在认证、授权内部实现机制中都有提到,最终处理都将交给Real进行处理*/
@Component
public class MyRealm extends AuthorizingRealm {public MyRealm() {super(new AllowAllCredentialsMatcher());setAuthenticationTokenClass(UsernamePasswordToken.class);//FIXME: 暂时禁用CachesetCachingEnabled(false);}@Autowiredprivate IUserService userService;/*** 权限授权是通过继承AuthorizingRealm抽象类,重载doGetAuthorizationInfo()* 当访问到页面的时候,链接配置了相应的权限或者shiro标签才会执行此方法否则不会执行,* 所以如果只是简单的身份认证没有权限的控制的话,那么这个方法可以不进行实现,直接返回null即可。* 在这个方法中主要是使用类:SimpleAuthorizationInfo进行角色的添加和权限的添加。* @author linyuanhuang* 16:27 2018/3/29* @return org.apache.shiro.authz.AuthorizationInfo*/@Overrideprotected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {User user = (User) principals.getPrimaryPrincipal();SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();User dbUser = userService.findByUserName(user.getUserName());Set<String> shiroPermissions = new HashSet<>();Set<String> roleSet = new HashSet<String>();Set<Role> roles = dbUser.getRoles();for (Role role : roles) {Set<Permission> resources = role.getResources();for (Permission resource : resources) {shiroPermissions.add(resource.getSourceKey());}roleSet.add(role.getRoleKey());}authorizationInfo.setRoles(roleSet);authorizationInfo.setStringPermissions(shiroPermissions);return authorizationInfo;}/*** Shiro的认证过程最终会交由Realm执行,这时会调用Realm的getAuthenticationInfo(token)方法* 该方法主要执行以下操作:1、检查提交的进行认证的令牌信息;2、根据令牌信息从数据源(通常为数据库)中获取用户信息;3、对用户信息进行匹配验证;4、验证通过将返回一个封装了用户信息的AuthenticationInfo实例;5、验证失败则抛出AuthenticationException异常信息。* @author linyuanhuang* 16:26 2018/3/29* @return org.apache.shiro.authc.AuthenticationInfo */@Overrideprotected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {String username = (String) token.getPrincipal();User user = userService.findByUserName(username);// 账号不存在if (user == null) {throw new UnknownAccountException("账号或密码不正确");}Object credentials = token.getCredentials();if (credentials == null) {throw new UnknownAccountException("账号或密码不正确");}String password = new String((char[]) credentials);// 密码错误if (!MD5Utils.md5(password).equals(user.getPassword())) {throw new IncorrectCredentialsException("账号或密码不正确");}// 账号锁定if (user.getLocked() == 1) {throw new LockedAccountException("账号已被锁定,请联系管理员");}SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user, password, getName());return info;}
}
登录控制层
@RequestMapping(value = {"/admin/login"}, method = RequestMethod.POST)public String login(@RequestParam("username") String username,@RequestParam("password") String password,ModelMap model) {try {Subject subject = SecurityUtils.getSubject();UsernamePasswordToken token = new UsernamePasswordToken(username, password);subject.login(token);return redirect("/admin/index");} catch (AuthenticationException e) {model.put("message", e.getMessage());}return "admin/login";}
这里就先介绍这么多,还有更多的内容如:shiro、freemarker的标签的使用这里就不一一介绍了,可以参考完整的例子:
GitHub地址:https://github.com/lyhkmm/spring-boot-examples/tree/master/spring-boot-shiro-freemarker
原文来自:lyhkmm.com
Spring Boot(八)——Shiro+FreeMarker相关推荐
- spring boot结合shiro实现用户-角色-权限的控制(包含用户名密码登陆和手机号验证码登陆)
spring boot整合shiro实现权限校验 1.首先导入项目所需jar包 <parent><groupId>org.springframework.boot</gr ...
- Spring Boot 模板引擎FreeMarker集成
Spring Boot 模板引擎FreeMarker集成 一.FreeMaker介绍 FreeMarker是一款免费的Java模板引擎,是一种基于模板和数据生成文本(HMLT.电子邮件.配置文件.源代 ...
- 解决Spring Boot集成Shiro,配置类使用Autowired无法注入Bean问题
为什么80%的码农都做不了架构师?>>> 如题,最近使用spring boot集成shiro,在shiroFilter要使用数据库动态给URL赋权限的时候,发现 @Autowi ...
- Spring Boot整合Shiro + Springboot +vue
目录 02 Spring Boot整合Shiro p1.shiro概述 1 什么是Shiro 2 Shiro核心组件 p2.Shiro实现登录认证 AccountRealm.java QueryWra ...
- Spring Boot 整合 shiro 之盐值加密认证详解(六)
Spring Boot 整合 shiro 之盐值加密认证详解 概述 不加盐认证 加入密码认证核心代码 修改 CustomRealm 新增获取密文的方法 修改 doGetAuthenticationIn ...
- Spring Boot 整合 Shiro(三)Kaptcha验证码 附源码
前言 本文是根据上篇<Spring Boot 整合Shiro(二)加密登录与密码加盐处理>进行修改,如有不明白的转上篇文章了解. 1.导入依赖 <!-- https://mvnrep ...
- 六、Spring Boot整合Shiro
六.Spring Boot整合Shiro 6.1.整合思路 6.2.创建spring boot项目 6.3.引入shiro依赖 6.4.配置shiro环境 创建配置类ShiroConfig 1.配置: ...
- 有手就行的 Spring Boot 集成 Shiro
前言 Apache Shiro 是 Java 的一个安全框架.目前,使用 Apache Shiro 的人越来越多,因为它相当简单,对比 Spring Security,可能没有 Spring Se ...
- Spring Boot + Vue + Shiro 实现前后端分离、权限控制
点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试资料 来源:http://sina.lt/gauR 本文总结自实习中对项 ...
- Spring Boot教程(十六):Spring Boot集成shiro
Apache Shiro™是一个功能强大且易于使用的Java安全框架,可执行身份验证,授权,加密和会话管理.借助Shiro易于理解的API,您可以快速轻松地保护任何应用程序 - 从最小的移动应用程序到 ...
最新文章
- SQL UPDATE SET FROM用法
- 专题3 标准IO编程
- 引号快捷键_高效率的Excel-Ctrl类快捷键二
- [云炬python3玩转机器学习]6-2模拟梯度下降法
- pandas中apply与map的异同
- 十四种Java开发工具点评
- 窄带语谱图c语言算法,MELP语音编码算法实现及算法改进
- mysql always as_MySQL always returning BIT values as blank
- [Android]HttpPost之post请求传递Json数据
- Glomosim安装和ParseC的使用等相关链接
- Python 紧急修复远程代码执行漏洞
- 如何养成周回顾习惯的回复
- mysql插入数据显示:Incorrect datetime value: '0000-00-00 00:00:00'
- 基于Netty手写RPC框架
- ME525+在线 刷机
- 通过SQL写FSG报表
- EasyX安装与使用
- Android Q SQLite性能问题调研
- 【ASP.NET Web】项目实践—网上宠物店8:制作“商品展示”页面
- 东方博宜OJ 1043 - 【入门】行李托运价格
热门文章
- oracle去空格和换行,ORACLE 中去回车、空格、TAB的函数
- c语言运行太短怎么毡筒,C语言程序设计 最简单的C程序设计.ppt
- 计算机组成原理—第4章数值的机器运算
- Deep Adversarial Decomposition: A Unified Framework for Separating Superimposed Images 论文阅读笔记
- 【初学大数据】CentOS7安装hadoop3.3.2完全分布式详细流程
- matlab 函数变量保存在工作区,Matlab中保存函数内部中间变量到工作空间的两种方法...
- C语言方差和标准差公式,方差概念及计算公式.docx
- google国内镜像网址收集
- ae合成设置快捷键_AE脚本使用快捷键控制关键帧操作 Keyboard v1.2.1 + 使用教程【资源分享1081】...
- 怎么用java打开pkg文件怎么打开,pkg文件怎么打开
【处置方式】