1. 概述
1.1 SpringBoot
这个就没什么好说的了,能看到这个教程的,估计都是可以说精通了SpringBoot
的使用
1.2 Shiro
一个安全框架,但不只是一个安全框架。它能实现多种多样的功能。并不只是局限在web层。在国内的市场份额占比高于SpringSecurity
,是使用最多的安全框架
可以实现用户的认证和授权。比SpringSecurity
要简单的多。
1.3 Jwt
我的理解就是可以进行客户端与服务端之间验证的一种技术,取代了之前使用Session来验证的不安全性
为什么不适用Session?
原理是,登录之后客户端和服务端各自保存一个相应的SessionId,每次客户端发起请求的时候就得携带这个SessionId来进行比对
- Session在用户请求量大的时候服务器开销太大了
- Session不利于搭建服务器的集群(也就是必须访问原本的那个服务器才能获取对应的SessionId)
它使用的是一种令牌技术
Jwt字符串分为三部分
-
Header
存储两个变量
- 秘钥(可以用来比对)
- 算法(也就是下面将Header和payload加密成Signature)
-
payload
存储很多东西,基础信息有如下几个
- 签发人,也就是这个“令牌”归属于哪个用户。一般是
userId
- 创建时间,也就是这个令牌是什么时候创建的
- 失效时间,也就是这个令牌什么时候失效
- 唯一标识,一般可以使用算法生成一个唯一标识
- 签发人,也就是这个“令牌”归属于哪个用户。一般是
-
Signature
这个是上面两个经过Header中的算法加密生成的,用于比对信息,防止篡改Header和payload
然后将这三个部分的信息经过加密生成一个JwtToken
的字符串,发送给客户端,客户端保存在本地。当客户端发起请求的时候携带这个到服务端(可以是在cookie
,可以是在header
,可以是在localStorage
中),在服务端进行验证
好了,废话不多说了,下面开始实战,实战分为以下几个部分
SpringBoot
整合Shiro
SpringBoot
整合Jwt
SpringBoot
+Shiro
+Jwt
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>3.11.0</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
2. SpringBoot整合Shiro
两种方式:
- 将ssm的整合的配置使用java代码方式在springBoot中写一遍
- 使用官方提供的start
2.1 使用start整合springBoot
pom.xml
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-web-starter</artifactId>
<version>1.4.0</version>
</dependency>
<!--注意不要写成shiro-spring-boot-starter-->
application.properties
shiro.loginUrl="xxx"
#认证不通过的页面
shiro.UnauthorizedUrl="xxx"
#授权不通过的跳转页面
创建ShiroConfig.java进行一些简单的配置
@Configuration
public class SpringShiroConfig {
@Bean
public Realm customRealm() {
return new CustomRealm();
}
@Bean
public DefaultWebSecurityManager securityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(customRealm());
// 关闭 ShiroDAO 功能
DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO();
DefaultSessionStorageEvaluator defaultSessionStorageEvaluator = new DefaultSessionStorageEvaluator();
// 不需要将 Shiro Session 中的东西存到任何地方(包括 Http Session 中)
defaultSessionStorageEvaluator.setSessionStorageEnabled(false);
subjectDAO.setSessionStorageEvaluator(defaultSessionStorageEvaluator);
securityManager.setSubjectDAO(subjectDAO);
return securityManager;
}
@Bean
public ShiroFilterChainDefinition shiroFilterChainDefinition() {
DefaultShiroFilterChainDefinition chain = new DefaultShiroFilterChainDefinition();
// 哪些请求可以匿名访问
chain.addPathDefinition("/login", "anon"); // 登录接口
chain.addPathDefinition("/notLogin", "anon"); // 未登录错误提示接口
chain.addPathDefinition("/403", "anon"); // 权限不足错误提示接口
// 除了以上的请求外,其它请求都需要登录
chain.addPathDefinition("/**", "authc");
return chain;
}
// Shiro 和 Spring AOP 整合时的特殊设置
@Bean
public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator creator = new DefaultAdvisorAutoProxyCreator();
creator.setProxyTargetClass(true);
return creator;
}
}
//还有关闭ShiroDao功能
创建自定义的Realm
public class CustomRealm extends AuthorizingRealm {
private static final Set<String> tomRoleNameSet = new HashSet<>();
private static final Set<String> tomPermissionNameSet = new HashSet<>();
private static final Set<String> jerryRoleNameSet = new HashSet<>();
private static final Set<String> jerryPermissionNameSet = new HashSet<>();
static {
tomRoleNameSet.add("admin");
jerryRoleNameSet.add("user");
tomPermissionNameSet.add("user:insert");
tomPermissionNameSet.add("user:update");
tomPermissionNameSet.add("user:delete");
tomPermissionNameSet.add("user:query");
jerryPermissionNameSet.add("user:query");
}
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
String username = (String) principals.getPrimaryPrincipal();
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
if (username.equals("tom")) {
info.addRoles(tomRoleNameSet);
info.addStringPermissions(tomPermissionNameSet);
} else if (username.equals("jerry")) {
info.addRoles(jerryRoleNameSet);
info.addStringPermissions(jerryPermissionNameSet);
}
return info;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
String username = (String) token.getPrincipal();
if (username == null)