package com.shirodemo.bean;
import java.io.Serializable;
public class User implements Serializable {
private int id;
private String username;
private String password;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
}
自定义sessionManager:
package com.shirodemo.common;
import org.apache.shiro.web.servlet.ShiroHttpServletRequest;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.apache.shiro.web.util.WebUtils;
import org.springframework.util.StringUtils;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.Serializable;
public class MySessionManager extends DefaultWebSessionManager {
private static final String AUTHORIZATION = "Authorization";//授权
private static final String REFERENCED_SESSION_ID_SOURCE = "Stateless request";
public MySessionManager() {
super();
}
/**
*
* @param request
* @param response
* @return
* @decribe 重写getSessionId作为token,然后保存在redis里面,用户下次验证
*/
@Override
protected Serializable getSessionId(ServletRequest request, ServletResponse response) {
//获得请求头的token
String id = WebUtils.toHttp(request).getHeader(AUTHORIZATION);
//选择在ajax的请求头中传递sessionId,前后端分离选择在ajax请求头中获得sessionid
if(!StringUtils.isEmpty(id))
{
request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_SOURCE, REFERENCED_SESSION_ID_SOURCE);
request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, id);
request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE);
return id;
}
else
{
//默认是从cookie中得到sessionid
return super.getSessionId(request, response);
}
}
}
ShiroConfig:
package com.shirodemo.config;
import com.shirodemo.Realm.UserRealm;
import com.shirodemo.common.MySessionManager;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.session.mgt.eis.JavaUuidSessionIdGenerator;
import org.apache.shiro.session.mgt.eis.SessionIdGenerator;
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.session.SessionListener;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.crazycake.shiro.RedisCacheManager;
import org.crazycake.shiro.RedisManager;
import org.crazycake.shiro.RedisSessionDAO;
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 org.apache.shiro.mgt.SecurityManager;
import java.util.LinkedHashMap;
import java.util.Map;
//Shiro的配置类
@Configuration
public class ShiroConfig {
/**
* Shiro的核心api
* Subject:用户主体(把操作交给SecurityManager)
* SecurityManager:安全管理器(关联Realm)
* Realm:Shiro连接数据的桥梁
*/
/**
* 创建ShiroFilterFactoryBean
* @param
* @return
*/
@Bean(name = "shiroFilter")
public ShiroFilterFactoryBean shiroFilter(@Qualifier("securityManager") SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
// Map filters = shiroFilterFactoryBean.getFilters();
// filters.put("perms", new RestAuthorizationFilter());
//设置安全管理器
shiroFilterFactoryBean.setSecurityManager(securityManager);
//配置未认证跳转页面
//使用shiro内置过滤器拦截
/**
* 常用的过滤器
* anon:无须认证(登录)可以访问
* authc:必须认证才可以访问
* user: 如果使用remenberMe的功能才可以直接访问
* perms:该资源必须得到资源权限才可以访问
* role:该资源得到角色权限才可以访问
*/
shiroFilterFactoryBean.setLoginUrl("/backLogin");
//配置未授权跳转页面
shiroFilterFactoryBean.setUnauthorizedUrl("/unAthc");
Map<String,String> filterMap = new LinkedHashMap<String,String>();
filterMap.put("/login","anon");//拦截根目录
filterMap.put("/swagger-ui.html","anon");
filterMap.put("/static/**", "anon");
filterMap.put("/swagger/**","anon");
filterMap.put("/webjars/**", "anon");
filterMap.put("/swagger-resources/**","anon");
filterMap.put("/v2/**","anon");
filterMap.put("/img","anon");//文件资源放行
filterMap.put("/doLogin","anon");//拦截根目录
//资源授权例子
//添加需要授权的资源
filterMap.put("/role","perms[role:list]");
filterMap.put("/user","perms[user:list]");
filterMap.put("/user/addUser","perms[user:add]");//用户添加权限
filterMap.put("/user/updateUser","perms[user:update]");//用户更新权限
filterMap.put("/*","authc");//拦截根目录
//未登录,shiro应重定向到登录界面,此处返回未登录状态信息由前端控制跳转页面
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);
return shiroFilterFactoryBean;
}
@Bean
public DefaultWebSessionManager sessionManager()
{
//DefaultWebSessionManager defaultWebSessionManager = new DefaultWebSessionManager();
MySessionManager mySessionManager = new MySessionManager(); //使用自定义的session管理器
mySessionManager.setCacheManager(cacheManager()); //设置缓存管理器
mySessionManager.setSessionDAO(redisSessionDAO());//设置sessionDAO
mySessionManager.setSessionIdUrlRewritingEnabled(true);
return mySessionManager;
}
/**
* 创建Realm,需要自定义
* @return
*/
@Bean(name = "userRealm")
public UserRealm getRealm()
{
UserRealm userRealm = new UserRealm();
//userRealm.setCredentialsMatcher(credentialsMatcher());
userRealm.setCachingEnabled(true); //开启缓存
return userRealm;
}
/**
* 盐值加密
* @return
*/
// @Bean(name="credentialsMatcher")
// public HashedCredentialsMatcher credentialsMatcher()
// {
// HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher();
//
// hashedCredentialsMatcher.setHashAlgorithmName("MD5"); //加密算法名
//
// hashedCredentialsMatcher.setHashIterations(2); //加密次数
//
// hashedCredentialsMatcher.setStoredCredentialsHexEncoded(true); //采用hash散列算法加密
//
// return hashedCredentialsMatcher;
// }
/**
* 安全管理器
* @param userRealm
* @return
*/
@Bean(name = "securityManager")
public SecurityManager getDefaultSecurityManager(@Qualifier("userRealm") UserRealm userRealm){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
//配置自定义session管理,使用redis
securityManager.setSessionManager(sessionManager());
//配置缓存,使用redis
securityManager.setCacheManager(cacheManager());
securityManager.setRealm(getRealm());
return securityManager;
}
/**
* 生命周期处理器
* @return
*/
@Bean(name="lifecycleBeanPostProcessor")
public LifecycleBeanPostProcessor lifecycleBeanPostProcessor(){
return new LifecycleBeanPostProcessor();
}
/**
*
* redisManager管理器
* @return
*/
@Bean
public RedisManager redisManager(){
RedisManager redisManager = new RedisManager();
//redis配置
redisManager.setHost("127.0.0.1");
redisManager.setPort(6379);
redisManager.setPassword("123456");
return redisManager;
}
/**
* shiro缓存管理器,
*
* @return
*/
public RedisCacheManager cacheManager()
{
RedisCacheManager redisCacheManager = new RedisCacheManager();
redisCacheManager.setRedisManager(redisManager());
redisCacheManager.setPrincipalIdFieldName("username");//根据用户名缓存
redisCacheManager.setExpire(200000);//设置缓存时间
return redisCacheManager;
}
/*
* session dao
* @return
*/
@Bean
public RedisSessionDAO redisSessionDAO()
{
RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
redisSessionDAO.setRedisManager(redisManager());
return redisSessionDAO;
}
}
登录接口:
package com.shirodemo.controller;
import com.alibaba.fastjson.JSONObject;
import com.shirodemo.bean.User;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class LoginController {
@PostMapping("/doLogin")
public String doLogin(@RequestBody User user)
{
JSONObject jsonObject = new JSONObject();
Subject subject = SecurityUtils.getSubject();
//根据用户名和密码生成token
UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(user.getUsername(),user.getPassword());
try{
subject.login(usernamePasswordToken);
jsonObject.put("code",200);
jsonObject.put("msg","登录成功");
jsonObject.put("token",subject.getSession().getId());//把token发送给前端
}
catch (IncorrectCredentialsException e)
{
jsonObject.put("msg","密码错误");
}
catch (LockedAccountException e)
{
jsonObject.put("msg","用户账号已被冻结");
}
catch (UnknownAccountException e)
{
jsonObject.put("msg","该用户不存在");
}
return jsonObject.toJSONString();
}
@GetMapping(value= "/unAthc")
public String unAthc()
{
System.out.println("跳转到未授权页面");
JSONObject jsonObject = new JSONObject();
jsonObject.put("mycode","403");
jsonObject.put("msg","未授权");
return jsonObject.toJSONString();
}
}
UserMapper:
package com.shirodemo.dao;
import com.shirodemo.bean.User;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface UserMapper {
User getUserByUsername(String username);
}
UserService:
package com.shirodemo.service;
import com.shirodemo.bean.User;
public interface UserService {
User getUserByUsername(String username);
}
UserServiceImpl
package com.shirodemo.service.impl;
import com.shirodemo.bean.User;
import com.shirodemo.dao.UserMapper;
import com.shirodemo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl implements UserService {
@Autowired
UserMapper userMapper;
@Override
public User getUserByUsername(String username) {
return userMapper.getUserByUsername(username);
}
}
自定义UserRealm
package com.shirodemo.Realm;
import com.shirodemo.bean.User;
import com.shirodemo.service.UserService;
import org.apache.shiro.SecurityUtils;
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.apache.shiro.subject.Subject;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class UserRealm extends AuthorizingRealm {
@Autowired
UserService tbUserService;
/**
*
* @param principalCollection
* @return
* @describe执行授权逻辑
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("执行授权逻辑");
//给当前登录用户授权
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
// Subject subject = SecurityUtils.getSubject();
// //得到User
// User tbUser = (User)subject.getPrincipal();
// //我这里是从数据库中得到权限字符
// //例如我数据库中保存的是user:list这样的字符
// System.out.println(tbUser);
// List authority = tbUserService.getAuthorityByName(tbUser.getUsername());
// System.out.println(authority);
// /**
// * 1:先查出用户名
// * 2:根据用户名查询出用户id
// * 3:根据用户id查询出role_id
// * 4:根据role_id查询出perms_id
// * 5:根据perms_id查询具体权限
// * 6:然后封装在一个set中进行授权处理
// */
// Set set = new HashSet();
// for(String perms : authority)
// {
// if(null != perms)
// set.add(perms);
// }
//
// info.setStringPermissions(set);
return info;
}
/**
*
* @param authenticationToken
* @return
* @throws AuthenticationException
* @decribe 执行认证(登录)逻辑
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("执行认证(登录)逻辑");
UsernamePasswordToken token = (UsernamePasswordToken) authenticationToken;
//System.out.println(token.getUsername());
//需要加密和解密
User tbUser= (User)tbUserService.getUserByUsername(token.getUsername());
//System.out.println(tbUser);
String password = tbUser.getPassword();
//这里直接拿Username生成盐值,也可以自定义
//ByteSource credentialsSalt = ByteSource.Util.bytes(tbUser.getUsername());
if(("").equals(password))
{
// throw new AuthenticationException();
return null; //shiro底层会返回一个UnknownAccountException
}
return new SimpleAuthenticationInfo(tbUser,tbUser.getPassword(),getName());
}
}
yml:
server:
port: 8099
spring:
profiles:
active: dev
druid:
login:
username: root
password: root
---
spring:
profiles: dev
datasource:
driverClassName: com.mysql.jdbc.Driver
url: jdbc:mysql://localhost:3306/shirodemo?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=UTC
username: root
password : root
type: com.alibaba.druid.pool.DruidDataSource
#连接池配置
# 初始化大小,最小等待连接数量,最大等待连接数量,最大连接数
initialsize: 1
minidle: 1
maxidle: 5
maxActive: 20
#最长等待时间
maxWait: 6000
# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
timeBetweenEvictionRunsMillis: 60000
# 配置一个连接在池中最小生存的时间,单位是毫秒
minEvictableIdleTimeMillis: 300000
validationQuery: SELECT 1 FROM DUAL
testWhileIdle: true
testOnBorrow: true
testOnReturn: false
# 打开PSCache,并且指定每个连接上PSCache的大小
poolPreparedStatements: false
maxPoolPreparedStatementPerConnectionSize: 20
# 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
filters: stat,wall,log4j
# 通过connectProperties属性来打开mergeSql功能;慢SQL记录
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
# 合并多个DruidDataSource的监控数据
#spring.datasource.useGlobalDataSourceStat=true
---
mybatis-plus:
mapper-locations: classpath:mapper/*.xml
type-aliases-package: com.shirodemo.bean
pagehelper:
helperDialect: mysql
reasonable: true
supportMethodsArguments: true
pageSizeZero: false #pageSize=0
---
spring:
redis:
port: 6379
database: 0
host: 127.0.0.1
password: 123456
#默认为没有密码
jedis:
pool:
#连接池最大连接数
max-active: 8
#连接池最大阻塞时间(-1为没有限制)
max-wait: -1
#连接池最大空闲连接
max-idle: 5
#连接池最少空闲连接
min-idle: 0
timeout: 10000