最近做的项目整合了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();
}