技术标签: spring boot java restful shiro java学习笔记
最近做的项目整合了SpringBoot+Shiro,自己也不会,就就在网上现学现用,然后发现也有一篇满足的我需要的一篇完整帖子,所以有了这篇。废话少说。
还是先上一张图,大概了解一下shiro框架,有理论有实践。
对上图简单的进行说明
三个核心组件:Subject, SecurityManager 和 Realms.
Subject: 即“当前操作用户”。但是,在Shiro中,Subject这一概念并不仅仅指人,也可以是第三方进程、后台帐户(Daemon Account)或其他类似事物。它仅仅意味着“当前跟软件交互的东西”。
Subject代表了当前用户的安全操作,SecurityManager则管理所有用户的安全操作。
SecurityManager: 它是Shiro框架的核心,典型的Facade模式,Shiro通过SecurityManager来管理内部组件实例,并通过它来提供安全管理的各种服务。
Realm: Realm充当了Shiro与应用安全数据间的“桥梁”或者“连接器”。也就是说,当对用户执行认证(登录)和授权(访问控制)验证时,Shiro会从应用配置的Realm中查找用户及其权限信息。
从这个意义上讲,Realm实质上是一个安全相关的DAO:它封装了数据源的连接细节,并在需要时将相关数据提供给Shiro。当配置Shiro时,你必须至少指定一个Realm,用于认证和(或)授权。配置多个Realm是可以的,但是至少需要一个。
Shiro内置了可以连接大量安全数据源(又名目录)的Realm,如LDAP、关系数据库(JDBC)、类似INI的文本配置资源以及属性文件等。如果系统默认的Realm不能满足需求,你还可以插入代表自定义数据源的自己的Realm实现。
话不多说 撸代码
我的项目目录结构
导入shiro关键依赖
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-web-starter</artifactId>
<version>1.4.2</version>
</dependency>
<!-- shiro ehcache 缓存-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-ehcache</artifactId>
<version>1.4.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/cn.hutool/hutool-all -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.7.9</version>
</dependency>
自定义Realm
package com.xiang.springboot_shiro.config.shiro;
import com.xiang.springboot_shiro.entity.model.User;
import com.xiang.springboot_shiro.service.UserService;
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.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;
public class CustomerRealm extends AuthorizingRealm {
@Autowired
private UserService userService;
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
return info;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
String principal = token.getPrincipal().toString();
User user = userService.selectUserByName(principal);
if (user == null){
return null;
}else {
String username = user.getUsername();
String password = user.getPassword();
//2、用户名。 2、密码 3、realm,即当前realm的名称
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(username,password,getName());
return info;
}
}
}
shiroConfig
package com.xiang.springboot_shiro.config.shiro;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.mgt.SessionsSecurityManager;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
import java.util.Map;
@Configuration
public class shiroConfig {
/**
* 过滤器
* @param securityManager
* @return
*/
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager){
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
//设置安全管理器
shiroFilterFactoryBean.setSecurityManager(securityManager);
Map<String, String> map = new HashMap<>();
map.put("/login","anon");
shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
return shiroFilterFactoryBean;
}
/**
* 安全管理器
* @param customerRealm
* @return
*/
@Bean
public SessionsSecurityManager securityManager(CustomerRealm customerRealm){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
//设置自定义Realm
securityManager.setRealm(customerRealm);
return securityManager;
}
/**
* 自定义Realm
* @return
*/
@Bean
CustomerRealm customerRealm(){
CustomerRealm customerRealm = new CustomerRealm();
//凭证匹配器
customerRealm.setCredentialsMatcher(hashedCredentialsMatcher());
return customerRealm;
}
/**
* 凭证匹配器
* 若密码使用MD5加密后,一定设置密码匹配策略
* @return
*/
@Bean
public HashedCredentialsMatcher hashedCredentialsMatcher(){
HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
hashedCredentialsMatcher.setHashAlgorithmName("MD5");
hashedCredentialsMatcher.setHashIterations(1024);
return hashedCredentialsMatcher;
}
}
controller
package com.xiang.springboot_shiro.controller;
import cn.hutool.core.util.StrUtil;
import com.xiang.springboot_shiro.entity.model.User;
import com.xiang.springboot_shiro.service.UserService;
import com.xiang.springboot_shiro.utils.RestMessage;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import sun.security.util.Password;
import sun.text.normalizer.UBiDiProps;
@RestController
public class UserController {
@Autowired
private UserService userService;
@GetMapping("/login")
public RestMessage login(String username,String password){
//引入了hutool工具类
if (StrUtil.isBlank(username)){
return RestMessage.newInstance(false,"用户名不能为空",null);
}
if (StrUtil.isBlank(password)){
return RestMessage.newInstance(false,"密码不能为空",null);
}
//用户认证信息
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(username,password);
try {
//shiro登录接口,这里建议自己调试看一下,会进入我们自定义的realm的AuthenticationInfo
subject.login(token);
} catch (UnknownAccountException e) {
return RestMessage.newInstance(false,"用户名错误",null);
}catch (IncorrectCredentialsException e){
return RestMessage.newInstance(false,"密码错误",null);
}catch (AuthenticationException e){
return RestMessage.newInstance(false,"没有权限",null);
}
return RestMessage.newInstance(true,"登录成功",null);
}
}
测试结果
赠送一个消息返回工具类
package com.xiang.springboot_shiro.utils;
import java.io.IOException;
import java.io.Serializable;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
/**
* 接口返回消息
* @author xiang
*
*/
//@ApiModel(description= "返回响应数据")
public class RestMessage<T> implements Message,Serializable{
private static final long serialVersionUID = -1865510446859810360L;
//@ApiModelProperty(value = "是否成功")
private boolean success;
//@ApiModelProperty(value = "消息对象")
private String message;
//@ApiModelProperty(value = "消息代码")
private String code;
//@ApiModelProperty(value = "返回对象")
private T data;
public RestMessage(){
}
public static <T> RestMessage<T> newInstance(boolean success,String message,T data){
return new RestMessage<T>(success,message,data);
}
public static <T> RestMessage<T> newInstance(boolean success,String code,String message,T data){
return new RestMessage<T>(success,code,message,data);
}
public RestMessage(boolean success,String message,T data){
this.success = success;
this.message = message;
this.data = data;
this.code="200";
}
public RestMessage(boolean success,String code,String message,T data){
this.success = success;
this.message = message;
this.data = data;
this.code=code;
}
public boolean isSuccess() {
return success;
}
public void setSuccess(boolean success) {
this.success = success;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
@Override
public String toJsonString() {
ObjectMapper mapper = new ObjectMapper();
try {
return mapper.writeValueAsString(this);
} catch (JsonProcessingException e) {
e.printStackTrace();
return null;
}
}
public static <S> RestMessage<S> parseJsonString(String jsonstr,TypeReference<RestMessage<S>> typeReference) throws IOException{
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
RestMessage<S> rest = mapper.readValue(jsonstr, typeReference);
return rest;
}
public static void main(String[] args) throws IOException {
//String jsonstr = "{\"success\":true,\"message\":\"查询成功\",\"data\": [{\"id\": 50,\"number\":\"001\",\"name\": \"苹果\",\"price\": 12},{\"id\": 50,\"number\":\"001\",\"name\": \"苹果\",\"price\": 12}]}";
//String jsonstr = "{\"success\":true,\"message\":\"查询成功\",\"data\": {\"id\": 50,\"number\":\"001\",\"name\": \"苹果\",\"price\": 12}}";
//String jsonstr = "{\"success\":true,\"message\":\"查询成功\",\"data\": 123}";
//RestMessage<List<Product>> rest= RestMessage.parseJsonString(jsonstr,new TypeReference<RestMessage<List<Product>>>(){});
//List<Product> list = rest.getData();
//System.out.println(list.get(0).getName());
}
}
package com.xiang.springboot_shiro.utils;
public interface Message {
String toJsonString();
}
文章浏览阅读1k次。通过使用ajax方法跨域请求是浏览器所不允许的,浏览器出于安全考虑是禁止的。警告信息如下:不过jQuery对跨域问题也有解决方案,使用jsonp的方式解决,方法如下:$.ajax({ async:false, url: 'http://www.mysite.com/demo.do', // 跨域URL ty..._nginx不停的xhr
文章浏览阅读2k次。关于在 Oracle 中配置 extproc 以访问 ST_Geometry,也就是我们所说的 使用空间SQL 的方法,官方文档链接如下。http://desktop.arcgis.com/zh-cn/arcmap/latest/manage-data/gdbs-in-oracle/configure-oracle-extproc.htm其实简单总结一下,主要就分为以下几个步骤。..._extproc
文章浏览阅读1.5w次。linux下没有上面的两个函数,需要使用函数 mbstowcs和wcstombsmbstowcs将多字节编码转换为宽字节编码wcstombs将宽字节编码转换为多字节编码这两个函数,转换过程中受到系统编码类型的影响,需要通过设置来设定转换前和转换后的编码类型。通过函数setlocale进行系统编码的设置。linux下输入命名locale -a查看系统支持的编码_linux c++ gbk->utf8
文章浏览阅读750次。今天准备从生产库向测试库进行数据导入,结果在imp导入的时候遇到“ IMP-00009:导出文件异常结束” 错误,google一下,发现可能有如下原因导致imp的数据太大,没有写buffer和commit两个数据库字符集不同从低版本exp的dmp文件,向高版本imp导出的dmp文件出错传输dmp文件时,文件损坏解决办法:imp时指定..._imp-00009导出文件异常结束
文章浏览阅读143次。当下是一个大数据的时代,各个行业都离不开数据的支持。因此,网络爬虫就应运而生。网络爬虫当下最为火热的是Python,Python开发爬虫相对简单,而且功能库相当完善,力压众多开发语言。本次教程我们爬取前程无忧的招聘信息来分析Python程序员需要掌握那些编程技术。首先在谷歌浏览器打开前程无忧的首页,按F12打开浏览器的开发者工具。浏览器开发者工具是用于捕捉网站的请求信息,通过分析请求信息可以了解请..._初级python程序员能力要求
文章浏览阅读7.6k次,点赞2次,收藏6次。@Service标注的bean,类名:ABDemoService查看源码后发现,原来是经过一个特殊处理:当类的名字是以两个或以上的大写字母开头的话,bean的名字会与类名保持一致public class AnnotationBeanNameGenerator implements BeanNameGenerator { private static final String C..._@service beanname
文章浏览阅读6.9w次,点赞73次,收藏463次。1.前序创建#include<stdio.h>#include<string.h>#include<stdlib.h>#include<malloc.h>#include<iostream>#include<stack>#include<queue>using namespace std;typed_二叉树的建立
文章浏览阅读7.1k次。在Asp.net上使用Excel导出功能,如果文件名出现中文,便会以乱码视之。 解决方法: fileName = HttpUtility.UrlEncode(fileName, System.Text.Encoding.UTF8);_asp.net utf8 导出中文字符乱码
文章浏览阅读2.1k次,点赞4次,收藏23次。第一次实验 词法分析实验报告设计思想词法分析的主要任务是根据文法的词汇表以及对应约定的编码进行一定的识别,找出文件中所有的合法的单词,并给出一定的信息作为最后的结果,用于后续语法分析程序的使用;本实验针对 PL/0 语言 的文法、词汇表编写一个词法分析程序,对于每个单词根据词汇表输出: (单词种类, 单词的值) 二元对。词汇表:种别编码单词符号助记符0beginb..._对pl/0作以下修改扩充。增加单词
文章浏览阅读773次。我在使用adb.exe时遇到了麻烦.我想使用与bash相同的adb.exe shell提示符,所以我决定更改默认的bash二进制文件(当然二进制文件是交叉编译的,一切都很完美)更改bash二进制文件遵循以下顺序> adb remount> adb push bash / system / bin /> adb shell> cd / system / bin> chm..._adb shell mv 权限
文章浏览阅读6.8k次,点赞12次,收藏125次。1. 单目相机标定引言相机标定已经研究多年,标定的算法可以分为基于摄影测量的标定和自标定。其中,应用最为广泛的还是张正友标定法。这是一种简单灵活、高鲁棒性、低成本的相机标定算法。仅需要一台相机和一块平面标定板构建相机标定系统,在标定过程中,相机拍摄多个角度下(至少两个角度,推荐10~20个角度)的标定板图像(相机和标定板都可以移动),即可对相机的内外参数进行标定。下面介绍张氏标定法(以下也这么称呼)的原理。原理相机模型和单应矩阵相机标定,就是对相机的内外参数进行计算的过程,从而得到物体到图像的投影_相机-投影仪标定
文章浏览阅读2.2k次。文章目录Wayland 架构Wayland 渲染Wayland的 硬件支持简 述: 翻译一篇关于和 wayland 有关的技术文章, 其英文标题为Wayland Architecture .Wayland 架构若是想要更好的理解 Wayland 架构及其与 X (X11 or X Window System) 结构;一种很好的方法是将事件从输入设备就开始跟踪, 查看期间所有的屏幕上出现的变化。这就是我们现在对 X 的理解。 内核是从一个输入设备中获取一个事件,并通过 evdev 输入_wayland