最近的三孩儿政策有点儿意思 ,更有意思的是段子手,群聊大佬用这个来对对子真的太牛批了,在这记下来,以后都能说相声用了:
上联不说,自行脑补,下联:现实里,钱没挣,妻没娶,问我三胎?不生不生
勤勤恳恳劳劳碌碌终终九九六六,轻轻松松快快乐乐鱼鱼躺躺平平
十口心思,思房思车思工作;八方投历,历长历幼历坟殇
我上班奋斗,积累一身资本;你下班相亲,钱财全给岳母
你家生娃去种树,种完树来当社畜
人口红利入我肚,国泰民安展宏图
哈哈哈真是有够好笑嗷,言归正传,先记下springboot如何集成jsp。。。
访问localhost:8888/shiro/index.jsp 就会出现hello world了
现在,开始记录shiro整合springboot和数据库的认证授权:首先,写一个web拦截器,这没什么,像以往在配置包里写类然后别忘了注入到IOC即可,这里要多加的一步就是需要把安全管理器作为参数引入到拦截器的方法中,还要注意在springbootweb中安全管理器是这么声明的:DefaultWebSecurityManager(pom中引的包也略有不同):
<!--引入shiro整合springboot依赖-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-starter</artifactId>
<version>1.7.1</version>
<!--和maven项目引入的包不一样-->
</dependency>
package com.jin.springboot_jsp_shiro.config;
import com.jin.springboot_jsp_shiro.shiro.realms.CustomerRealm;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import java.util.HashMap;
import java.util.Map;
/**
* @author jinyunlong
* @date 2021/6/1 19:10
* @profession ICBC锅炉房保安
*/
//用来整合shiro框架相关的配置类,使拦截生效要一层套一层,不同于maven项目,这里要把安全管理器SecurityManager放进ShiroFilterFactoryBean中
@Configuration
public class ShiroConfig {
//1.创建shiroFilter //负责拦截所有请求
@Bean
public ShiroFilterFactoryBean getShiroFilterFactoryBean(DefaultWebSecurityManager defaultWebSecurityManager){
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
//给filter设置安全管理器
shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);
//配置系统公共资源
//配置系统受限资源
Map<String,String> map = new HashMap<String,String>();
map.put("/user/login","anon");//anon设置为公共资源,要放拦截资源上头
map.put("/**","authc"); //authc 请求这个资源需要认证和授权
// /**除了login全部需要用户认证
//默认认证界面路径
shiroFilterFactoryBean.setLoginUrl("/login.jsp"); //你要是改成login1.jsp就找不到喽,因为没写
shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
return shiroFilterFactoryBean;
}
//2.创建安全管理器
@Bean
public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("getRealm") Realm realm){
DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
//给安全管理器设置realm
defaultWebSecurityManager.setRealm(realm);
return defaultWebSecurityManager;
}
//3.创建自定义realm
@Bean
public Realm getRealm(){
CustomerRealm customerRealm = new CustomerRealm();
return customerRealm;
}
}
可以看到shiro对拦截器的好多东西都已经整好了,拿来就调,要注意安全管理器的形参要
"@Qualifier("getRealm") Realm realm"一下,这里把index.jsp配置了受限,所以要访问的话会被先定回login.jsp。
认证和退出的controller,这里要注意获取主体对象subject时,,在web环境中,只要创建了安全管理器,就会自动注入web的安全管理器,然后去拦截器代码里看看具体对这几个接口的拦截规则就行。(去看看拦截器类里头的注释)
package com.jin.springboot_jsp_shiro.controller;
import org.apache.shiro.SecurityUtils;
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.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* @author jinyunlong
* @date 2021/6/2 15:21
* @profession ICBC锅炉房保安
*/
//用来处理身份认证
@Controller
@RequestMapping("user")
public class UserController {
//退出登录
@RequestMapping("logout")
public String logout(){
Subject subject = SecurityUtils.getSubject();
subject.logout(); //退出用户
return "redirect:/login.jsp";
}
@RequestMapping("login")
public String login(String username,String password){
//获取主体对象,在web环境中,只要创建了安全管理器,就会自动注入web的安全管理器
Subject subject = SecurityUtils.getSubject();
try {
subject.login(new UsernamePasswordToken(username,password));
return "redirect:/index.jsp";
} catch (UnknownAccountException e) {
e.printStackTrace();
System.out.println("用户名错误");
} catch (IncorrectCredentialsException e) {
e.printStackTrace();
System.out.println("密码错误");
}
return "redirect:/login.jsp";
}
}
有一点是业务对象(UserService)是交给工厂管理的,自定义realm没法直接注入业务对象,所以需要自己从工厂中去取, 编写util:
package com.jin.springboot_jsp_shiro.utils;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
/**
* @author jinyunlong
* @date 2021/6/2 17:29
* @profession ICBC锅炉房保安
*/
@Component
public class ApplicationContextUtil implements ApplicationContextAware {
private static ApplicationContext context;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
//以后springboot启动完了,就会把创建好的工厂以参数形式传回这个方法
context = applicationContext;
}
//根据bean的名字获取工厂中的指定bean对象
public static Object getBean(String beanName){
Object bean = context.getBean(beanName);
return bean;
}
}
然后就是去自定义realm中实现调用了,注意getBean的名字是userService可以从业务类Service注解中自定义
package com.jin.springboot_jsp_shiro.shiro.realms;
import com.jin.springboot_jsp_shiro.entity.User;
import com.jin.springboot_jsp_shiro.service.UserService;
import com.jin.springboot_jsp_shiro.utils.ApplicationContextUtil;
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.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.util.ObjectUtils;
/**
* @author jinyunlong
* @date 2021/6/2 13:53
* @profession ICBC锅炉房保安
*/
public class CustomerRealm extends AuthorizingRealm {
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("===============================");
//根据身份信息
String principal = (String) authenticationToken.getPrincipal();
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo();
//在工厂中获取service对象
UserService userService = (UserService) ApplicationContextUtil.getBean("userService");
System.out.println("userService:"+userService);
User user = userService.findByUserName(principal);
// if("xiaojin".equals(principal)){
// return new SimpleAuthenticationInfo(principal,"123",this.getName());
// }
if(!ObjectUtils.isEmpty(user)){
return new SimpleAuthenticationInfo(user.getUsername(),user.getPassword(),
ByteSource.Util.bytes(user.getSalt()),this.getName());
}
return null;
}
}
注册后插表和根据用户名查数据库的逻辑就很简单了不说了,参照mybatis规范开发就行了,贴下语句
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.jin.springboot_jsp_shiro.dao.UserDAO">
<!--namespace根据自己需要创建的的mapper的路径和名称填写-->
<insert id="save" parameterType="User" keyProperty="id" useGeneratedKeys="true">
insert into t_user values (#{id},#{username},#{password},#{salt})
</insert>
<select id="findByUserName" parameterType="User" resultType="User">
select id,username,password,salt from t_user where username = #{username}
</select>
</mapper>
认证登录的逻辑就是注册后插表,然后登录时去表中根据输入用户名查是否存在与该用户名对应的用户实体,有的话返回实体对象,然后取密码和salt去进行密码验证。md5、散列均在自定义realm中修改,(加盐是在ByteSource.Util.bytes(user.getSalt())中)。都正确进入index界面,用户名或者密码错误都会抛出异常。
这节太长了,贴了好多代码,下节再说授权和缓存吧。