简介

在 Web 开发中,安全一直是非常重要的一个方面。

安全虽然属于应用的非功能性需求,但是应该在应用开发的初期就考虑进来。

市面上存在比较有名的:Shiro,Spring Security

首先我们看下它的官网介绍:Spring Security官网地址

Spring Security is a powerful and highly customizable authentication and access-control framework. It is the de-facto standard for securing Spring-based applications.

Spring Security is a framework that focuses on providing both authentication and authorization to Java applications. Like all Spring projects, the real power of Spring Security is found in how easily it can be extended to meet custom requirements

Spring Security是一个功能强大且高度可定制的身份验证和访问控制框架。它实际上是保护基于spring的应用程序的标准。

Spring Security是一个框架,侧重于为Java应用程序提供身份验证和授权。与所有Spring项目一样,Spring安全性的真正强大之处在于它可以轻松地扩展以满足定制需求

从官网的介绍中可以知道这是一个权限框架

Spring Security 基于 Spring 框架,提供了一套 Web 应用安全性的完整解决方案。

一般来说,Web 应用的安全性包括用户认证(Authentication)和用户授权(Authorization)两个部分,Spring Security 框架都有很好的支持。

在用户认证方面,Spring Security 框架支持主流的认证方式,包括 HTTP 基本认证、HTTP 表单验证、HTTP 摘要认证、OpenID 和 LDAP 等。在用户授权方面,

Spring Security 提供了基于角色的访问控制和访问控制列表(Access Control List,ACL),可以对应用中的领域对象进行细粒度的控制。

实战环境搭建

项目代码地址:https://gitee.com/zwtgit/spring-security-or-shiro

1、新建一个初始的springboot项目web模块,thymeleaf模块

2、导入静态资源,资源上面有

3、controller跳转!

@Controller
public class RouterController { @RequestMapping({"/","/index"})
public String index(){
return "index";
}
}

4、测试实验环境是否OK!

SpringSecurity

Spring Security 是针对Spring项目的安全框架,也是Spring Boot底层安全模块默认的技术选型,

他可以实现强大的Web安全控制,对于安全控制,我们仅需要引入 spring-boot-starter-security 模块,进行少量的配置,即可实现强大的安全管理!

记住几个类:

  • WebSecurityConfigurerAdapter:自定义Security策略
  • AuthenticationManagerBuilder:自定义认证策略
  • @EnableWebSecurity:开启WebSecurity模式

Spring Security的两个主要目标是 “认证” 和 “授权”(访问控制)。

“认证”(Authentication)

身份验证是关于验证您的凭据,如用户名/用户ID和密码,以验证您的身份。

身份验证通常通过用户名和密码完成,有时与身份验证因素结合使用。

“授权” (Authorization)

授权发生在系统成功验证您的身份后,最终会授予您访问资源(如信息,文件,数据库,资金,位置,几乎任何内容)的完全权限。

这个概念是通用的,而不是只在Spring Security 中存在。

认证和授权

1、引入 Spring Security 模块

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>

2、编写 Spring Security 配置类

参考官网:https://spring.io/projects/spring-security

查看我们自己项目中的版本,找到对应的帮助文档:

https://docs.spring.io/spring-security/site/docs/5.3.0.RELEASE/reference/html5 #servlet-applications 8.16.4

3、编写基础配置类

@EnableWebSecurity // 开启WebSecurity模式
public class SecurityConfig extends WebSecurityConfigurerAdapter { @Override
protected void configure(HttpSecurity http) throws Exception { }
}

4、定制请求的授权规则

@Override
protected void configure(HttpSecurity http) throws Exception {
// 定制请求的授权规则
// 首页所有人可以访问
http.authorizeRequests().antMatchers("/").permitAll()
.antMatchers("/level1/**").hasRole("vip1")
.antMatchers("/level2/**").hasRole("vip2")
.antMatchers("/level3/**").hasRole("vip3");
}

5、测试一下:发现除了首页都进不去了!因为我们目前没有登录的角色,因为请求需要登录的角色拥有对应的权限才可以!

6、在configure()方法中加入以下配置,开启自动配置的登录功能!

// 开启自动配置的登录功能
// /login 请求来到登录页
// /login?error 重定向到这里表示登录失败
http.formLogin();

7、测试一下:发现,没有权限的时候,会跳转到登录的页面!

8、查看刚才登录页的注释信息;

我们可以定义认证规则,重写configure(AuthenticationManagerBuilder auth)方法

//定义认证规则
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception { //在内存中定义,也可以在jdbc中去拿....
auth.inMemoryAuthentication()
.withUser("kuangshen").password("123456").roles("vip2","vip3")
.and()
.withUser("root").password("123456").roles("vip1","vip2","vip3")
.and()
.withUser("guest").password("123456").roles("vip1","vip2");
}

9、测试,我们可以使用这些账号登录进行测试!发现会报错!

There is no PasswordEncoder mapped for the id “null”

10、原因,我们要将前端传过来的密码进行某种方式加密,否则就无法登录,修改代码

//定义认证规则
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//在内存中定义,也可以在jdbc中去拿....
//Spring security 5.0中新增了多种加密方式,也改变了密码的格式。
//要想我们的项目还能够正常登陆,需要修改一下configure中的代码。我们要将前端传过来的密码进行某种方式加密
//spring security 官方推荐的是使用bcrypt加密方式。 auth.inMemoryAuthentication().passwordEncoder(new BCryptPasswordEncoder())
.withUser("zwt").password(new BCryptPasswordEncoder().encode("123456")).roles("vip2","vip3")
.and()
.withUser("root").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1","vip2","vip3")
.and()
.withUser("guest").password(new BCryptPasswordEncoder().encode("123456")).roles("vip1","vip2");
}

11、测试,发现,登录成功,并且每个角色只能访问自己认证下的规则!

权限控制和注销

1、开启自动配置的注销的功能

//定制请求的授权规则
@Override
protected void configure(HttpSecurity http) throws Exception {
//....
//开启自动配置的注销的功能
// /logout 注销请求
http.logout();
}

2、我们在前端,增加一个注销的按钮,index.html 导航栏中

3、我们可以去测试一下,登录成功后点击注销,发现注销完毕会跳转到登录页面!

4、但是,我们想让他注销成功后,依旧可以跳转到首页,该怎么处理呢?

// .logoutSuccessUrl("/"); 注销成功来到首页
http.logout().logoutSuccessUrl("/");

5、测试,注销完毕后,发现跳转到首页OK

6、我们现在又来一个需求:根据权限访问页面

我们需要结合thymeleaf中的一些功能

sec:authorize="isAuthenticated()":是否认证登录!来显示不同的页面

Maven依赖:

<!-- https://mvnrepository.com/artifact/org.thymeleaf.extras/thymeleaf-extras-springsecurity4 -->
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity5</artifactId>
<version>3.0.4.RELEASE</version>
</dependency>

7、修改我们的 前端页面

  1. 导入命名空间

8、重启测试,我们可以登录试试看,登录成功后确实,显示了我们想要的页面;

9、如果注销404了,就是因为它默认防止csrf跨站请求伪造,因为会产生安全问题,我们可以将请求改为post表单提交,或者在spring security中关闭csrf功能;

http.csrf().disable();//关闭csrf功能:跨站请求伪造,默认只能通过post方式提交logout请求
http.logout().logoutSuccessUrl("/");

权限控制和注销搞定!

记住我

1、开启记住我功能

//定制请求的授权规则
@Override
protected void configure(HttpSecurity http) throws Exception {
//。。。。。。。。。。。
//记住我
http.rememberMe();
}

原理: spring security 登录成功后,将cookie发送给浏览器保存,

以后登录带上这个cookie,只要通过检查就可以免登录了。

如果点击注销,则会删除这个cookie,具体的原理在JavaWeb阶段都讲过了,这里就不在多说了!

Shiro

Apache Shiro是一个Java的安全(权限)框架。

Shiro可以完成,认证,授权,加密,会话管理,Web集成,缓存等。

三大核心组件

Shiro有三大核心组件,即Subject、SecurityManager和Realm

组件
Subject 用户,认证主体 应用代码直接交互的对象是Subject,Subject。包含Principals和Credentials两个信息
SecurityManager 管理所有用户,为安全管理员。 是Shiro架构的核心。与Subject的所有交互都会委托给SecurityManager, Subject相当于是一个门面,而SecurityManager才是真正的执行者。它负责与Shiro 的其他组件进行交互。
Realm 连接数据,是一个域。 可以把Realm看成DataSource,即安全数据源。

Pricipals:代表身份。可以是用户名、邮件、手机号码等等,用来标识一个登陆主题的身份。

Credentials:代表凭证。常见的有密码、数字证书等等。

在官网的例子中了解Shiro

public class Quickstart {

    private static final transient Logger log = LoggerFactory.getLogger(Quickstart.class);

    public static void main(String[] args) {

        Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
SecurityManager securityManager = factory.getInstance();
SecurityUtils.setSecurityManager(securityManager); // 获取当前用户
Subject currentUser = SecurityUtils.getSubject(); // 通过当前用户拿到session
Session session = currentUser.getSession();
//在session中存值
session.setAttribute("someKey", "aValue");
String value = (String) session.getAttribute("someKey");
if (value.equals("aValue")) {
log.info("Retrieved the correct value! [" + value + "]");
} // 判断当前的用户是否被认证
if (!currentUser.isAuthenticated()) {
//token 令牌 随意设置
UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "vespa");
//设置记住我
token.setRememberMe(true);
try {
currentUser.login(token); //执行了登录操作
} catch (UnknownAccountException uae) { //用户名不存在
log.info("There is no user with username of " + token.getPrincipal());
} catch (IncorrectCredentialsException ice) { //密码错误
log.info("Password for account " + token.getPrincipal() + " was incorrect!");
} catch (LockedAccountException lae) { //用户被锁定
log.info("The account for username " + token.getPrincipal() + " is locked. " +
"Please contact your administrator to unlock it.");
}
// ... catch more exceptions here (maybe custom ones specific to your application?
catch (AuthenticationException ae) { //认证异常
//unexpected condition? error?
}
} //获取当前用户的认证
log.info("User [" + currentUser.getPrincipal() + "] logged in successfully."); //获得当前用户的角色
if (currentUser.hasRole("schwartz")) {
log.info("May the Schwartz be with you!");
} else {
log.info("Hello, mere mortal.");
} //是否拥有粗粒度(简单)权限
if (currentUser.isPermitted("lightsaber:wield")) {
log.info("You may use a lightsaber ring. Use it wisely.");
} else {
log.info("Sorry, lightsaber rings are for schwartz masters only.");
} //是否拥有更高权限
if (currentUser.isPermitted("winnebago:drive:eagle5")) {
log.info("You are permitted to 'drive' the winnebago with license plate (id) 'eagle5'. " +
"Here are the keys - have fun!");
} else {
log.info("Sorry, you aren't allowed to drive the 'eagle5' winnebago!");
} //注销
currentUser.logout();
//结束
System.exit(0);
}
}

快速上手

导入依赖

        <!--shiro-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.1</version>
</dependency>

编写shiro配置类

@Configuration
public class ShiroConfig { //shiroFilterBean 第三步 //DefaultWebSecurityManager 第二步 //创建Realm对象 需要自定义类 第一步
}

自定义Realm

//自定义Realm 需要继承AuthorizingRealm
public class UserRealm extends AuthorizingRealm {
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
System.out.println("执行了授权=>");
return null;
}
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
System.out.println("执行了认证");
return null;
}
}

继续编写shiro配置类

@Configuration
public class ShiroConfig { //ShiroFilterFactoryBean 第三步
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("manager") DefaultWebSecurityManager manager){
ShiroFilterFactoryBean filter = new ShiroFilterFactoryBean();
//设置安全管理器
filter.setSecurityManager(manager);
return filter;
} //DefaultWebSecurityManager 第二步
@Bean(name="manager")
public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
//关联UserRealm
manager.setRealm(userRealm);
return manager;
} //创建Realm对象 需要自定义类 第一步
@Bean
public UserRealm userRealm(){
return new UserRealm();
} }

实现登录拦截

//ShiroFilterFactoryBean 第三步
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("manager") DefaultWebSecurityManager manager){
ShiroFilterFactoryBean filter = new ShiroFilterFactoryBean();
//设置安全管理器
filter.setSecurityManager(manager);
//增加shiro内置过滤器
/**
* anon: 无需认证就可以访问
* authc: 必须认证才能访问
* user: 必须拥有记住我功能才能访问
* perms: 拥有对某个资源的权限才能访问
* role:拥有某个角色权限才能访问
*/
Map<String, String> map = new LinkedHashMap<>();
map.put("/user/add","authc");
map.put("/user/update","authc");
filter.setFilterChainDefinitionMap(map);
//设置登录页面
filter.setLoginUrl("/login");
return filter;
}

用户认证

   //用户登录功能
@PostMapping("/tologin")
public String tologin(String username, String password, Model model){
//获取用户
Subject subject = SecurityUtils.getSubject();
//封装用户登录数据并且生成一个token令牌
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
try{
subject.login(token);//登录 如果没有异常就说明OK了
return "index";//返回首页
}catch (UnknownAccountException e){
model.addAttribute("msg","用户名错误");
return "login";
}catch (IncorrectCredentialsException e){
model.addAttribute("msg","密码错误");
return "login";
} //认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
System.out.println("执行了认证");
//用户名密码 数据库中取
String name="admin";
String password="123";
UsernamePasswordToken userName=(UsernamePasswordToken)token;
if (!userName.getUsername().equals(name)){
return null;//抛出异常
}
//密码认证 shiro做
return new SimpleAuthenticationInfo("",password,"");
}

shiro整合mybais

导入依赖

      <!--spring boot整合mybatis-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.1</version>
</dependency>
<!--Mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!--SpringbootJDBC-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>

编写配置

spring:
thymeleaf:
cache: false #关闭模板引擎的缓存
# 配置数据源 serverTimezone=UTC 设置时区
datasource:
url: jdbc:mysql://localhost:3306/mybatis?serverTimezone=UTC&useSSL=true&useUnicode=true&characterEncoding=utf8
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver mybatis:
type-aliases-package: com.zwt.pojo
mapper-locations: classpath:mapper/*.xml
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

编写实体类,编写接口,编写映射文件

修改自定义Realm代码

 @Autowired
private UserMapper userMapper;
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
System.out.println("执行了认证");
UsernamePasswordToken userName=(UsernamePasswordToken)token;
//连接真实数据库
User user = userMapper.queryUserbyName(userName.getUsername());
if(user==null){ //没查出用户
return null;
}
//密码认证 shiro做 密码加密
return new SimpleAuthenticationInfo("",user.getPwd(),"");
}

请求授权

编写Shiro过滤器

1.设置访问/user/add路径时,需要[user:add权限

2.没有此权限访问时,会跳转指定路径

 //ShiroFilterFactoryBean 第三步
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(@Qualifier("manager") DefaultWebSecurityManager manager){
ShiroFilterFactoryBean filter = new ShiroFilterFactoryBean();
//设置安全管理器
filter.setSecurityManager(manager);
//增加shiro内置过滤器
/**
* anon: 无需认证就可以访问
* authc: 必须认证才能访问
* user: 必须拥有记住我功能才能访问
* perms: 拥有对某个资源的权限才能访问
* role:拥有某个角色权限才能访问
*/
Map<String, String> map = new LinkedHashMap<>();
// map.put("/user/add","authc");
map.put("/user/update","authc");
map.put("/user/add","perms[user:add]"); //访问此路径需要user:add权限
filter.setFilterChainDefinitionMap(map);
//设置登录页面
filter.setLoginUrl("/login");
filter.setUnauthorizedUrl("/noperms");//无权限时执行这个请求
return filter;
}

授权:

   //授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
System.out.println("执行了授权=>");
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.addStringPermission("user:add");//对每个用户进行授权 //取出用户信息
Subject subject = SecurityUtils.getSubject();
User principal = (User)subject.getPrincipal(); //将用户信息的权限信息 设置进去
//动态设置权限 需要新增一些数据库表 如:权限表
//info.addStringPermission(principal.getParms());
return info;
}

SpringSecurity-Shiro-初见的更多相关文章

  1. Shiro 核心功能案例讲解 基于SpringBoot 有源码

    Shiro 核心功能案例讲解 基于SpringBoot 有源码 从实战中学习Shiro的用法.本章使用SpringBoot快速搭建项目.整合SiteMesh框架布局页面.整合Shiro框架实现用身份认 ...

  2. java 知识结构

    JAVA基础阶段 阶段 技术名称 技术内容 T线 JavaSE JAVA 开发基础知识 | Eclipse 开发环境 | JavaSE 7.0 API | JavaSE 8.0新特性 | 多线程技术 ...

  3. SpringBoot微服务框架

    springboot 是什么? 配置如何编写 yaml 自动装配原理 集成Web开发 集成数据库Druid 分布式开发:Dubbo(RPC)+zookeeper swagger:接口文档 任务调度 S ...

  4. 安全框架 SpringSecurity 和 Shiro 对比

    突然再次很想理一下权限的事,但是实在不知道实际情况选哪个框架好,现在整理下网上的资料,做一下对比. 1.Spring-security 对spring 结合较好,如果项目用的springmvc ,使用 ...

  5. 安全框架Shiro和SpringSecurity的比较

    来自:https://www.cnblogs.com/zoli/p/11236799.html 两个基本的概念 安全实体:系统需要保护的具体对象数据 权限:系统相关的功能操作,例如基本的CRUD Sh ...

  6. springmvc+spring+mybatis+maven项目集成shiro进行用户权限控制【转】

    项目结构:   1.maven项目的pom中引入shiro所需的jar包依赖关系 ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 ...

  7. 【JavaWeb】Spring+SpringMVC+MyBatis+SpringSecurity+EhCache+JCaptcha 完整Web基础框架(四)

    SpringSecurity(1) 其实啊,这部分我是最不想写的,因为最麻烦的也是这部分,真的是非常非常的麻烦.关于SpringSecurity的配置,让我折腾了好半天,网上的配置方式一大把,但总有一 ...

  8. Shiro简单配置

    注:这里只介绍Spring配置模式. 因为官方例子虽然中有更加简洁的ini配置形式,但是使用ini配置无法与spring整合.而且两种配置方法一样,只是格式不一样. 涉及的jar包 核心包shiro- ...

  9. Spring MVC 急速集成 Shiro 实录

    相信有很多的程序员,不愿意进行用户管理这块代码实现. 原因之一,不同的JavaEE 系统,用户管理都会有个性化的实现,逻辑很繁琐. 而且是系统门面,以后背锅的几率非常大,可谓是低收益高风险. 最近在系 ...

  10. Apache Shiro和Spring Security的详细对比

    参考资料: 1)Apache Shiro Apache Shiro:http://shiro.apache.org/ 在Web项目中应用 Apache Shiro:http://www.ibm.com ...

随机推荐

  1. 单片机学习(一)项目的建立和vscode代码编辑环境的设置

    目录 Keil项目的建立 使用vscode进行开发 工欲善其事必先利其器,因此我们先搭建一个比较舒服的开发环境. Keil项目的建立 打开Keil软件点击Project/New uVision Pro ...

  2. Aging Cell两篇连发 | 华中科技大学王建枝团队运用蛋白质组学技术发现具有AD早期诊断价值的血小板生物标志物

    阿尔茨海默症 (Alzheimer 's disease,AD) 是一种原发性的中枢神经系统退行性疾病.AD的主要临床症状是缓慢的认知功能减退,包括记忆.逻辑推理能力和语言功能的进行性丟失,最后发展为 ...

  3. 内置函数 字符串的复制 strcpy

    1 #include<stdio.h> 2 #include<stdlib.h> 3 #include<string.h> 4 5 6 void main() 7 ...

  4. python自动化之(自动化测试报告)

    前言: 给予你们最关心的3步骤 什么是自动化测试报告?  答:在自动化测试过程中自动生成的测试报告 为什么要做自动生成测试报告? 答:真正的解放双手; 可以形成直观的测试结果; 给自己一个装X的机会; ...

  5. 中高级Android大厂面试秘籍,为你保驾护航金三银四,直通大厂(上)

    前言 当下,正面临着近几年来的最严重的互联网寒冬,听得最多的一句话便是:相见于江湖~.缩减HC.裁员不绝于耳,大家都是人心惶惶,年前如此,年后想必肯定又是一场更为惨烈的江湖厮杀.但博主始终相信,寒冬之 ...

  6. CYPEESS USB3.0程序解读之---GPIO

    CPRESS 官方给出的SDK1.1中(目前最新的SDK),提供了大量的例程供我们开发软件的时候作参考,就像STM32的开发一样提供了库一样,但是又不是库,仅仅是参考例程. 首先看一个简单一点的GPI ...

  7. LNMP 方式部署 zabbix 5.0

    文章链接 Zabbix 5.0 LTS新增功能 新版本附带了可用性,安全性和完整性方面的重大改进列表.Zabbix团队遵循的主要策略是使Zabbix尽可能可用.Zabbix是一种开源,免费的监视解决方 ...

  8. NOIP 模拟 $15\; \rm \text{玫瑰花精}$

    题解 \(by\;zj\varphi\) 一道线段树题目 这道题可以通过维护一棵线段树,线段树上的每个节点维护 \(\rm l,r,len,p\) 分别表示这段区间最左边的花精,最右边的花精,被两只花 ...

  9. express中session的基本使用

    1.首先安装express-session模块 npm install express-session --save 2.引入express-session,以及设置中间键 var session = ...

  10. spring生命周期的应用

    1.ApplicationContextAware 实现手工加载bean: 例:https://www.cnblogs.com/wala-wo/p/5119192.html https://www.c ...