springboot整合shiro多Realm控制
中国加油,武汉加油!
篇幅较长,配合右边目录观看
项目准备
- 创建springboot项目nz1904-springboot-08-manyroles
- 导入spring的web包,lombox,thymeleaf包及shiro包
<!-- spring跟shiro整合依赖--> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.3.2</version> </dependency>
1. 案例
1.1 自定义类增强UsernamePasswordToken
package com.wpj.token;
import org.apache.shiro.authc.UsernamePasswordToken;
public class CustomToken extends UsernamePasswordToken {
// 定义登录类型,为了在后面的时候中校验使用那个realm
private String loginType;
public CustomToken(String userName, String password, String loginType){
super(userName,password);
this.loginType = loginType;
}
public String getLoginType() {
return loginType;
}
public void setLoginType(String loginType) {
this.loginType = loginType;
}
}
1.2 自定义ModularRealmAuthenticator来选择使用哪个realm来进行认证
package com.wpj.authticator;
import com.wpj.token.CustomToken;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.pam.ModularRealmAuthenticator;
import org.apache.shiro.realm.Realm;
import java.util.ArrayList;
import java.util.Collection;
/**
* 在这里选择使用哪个realm来进行认证
*/
public class CustomModularRealmAuthenticator extends ModularRealmAuthenticator {
/**
* 通过传入数据类型来选择使用哪个realm
* @param authenticationToken
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doAuthenticate(AuthenticationToken authenticationToken) throws AuthenticationException {
// 做realm的校验
assertRealmsConfigured();
// 获取从前端传过来的token
CustomToken customToken = (CustomToken) authenticationToken;
// 获取登录类型
String loginType = customToken.getLoginType();
// 获取所有token
Collection<Realm> realms = getRealms();
// 登录类型对应多有realm全部获取到
Collection<Realm> typeRealms = new ArrayList<Realm>();
for (Realm realm: realms) {
// 类型对比
if (realm.getName().contains(loginType)) {
typeRealms.add(realm);
}
}
if(typeRealms.size() == 1) {
return doSingleRealmAuthentication(typeRealms.iterator().next(), customToken);
} else {
return doMultiRealmAuthentication(typeRealms,customToken);
}
}
}
1.3 定义一个登录类型的枚举
package com.wpj.type;
public enum LoginType {
USER("User"), ADMIN("Admin");
private String type;
private LoginType(String type) {
this.type = type;
}
@Override
public String toString() {
return this.type.toString();
}
}
1.5 application.properties配置
# 配置模板引擎
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html
spring.thymeleaf.mode=HTML5
spring.thymeleaf.encoding=utf-8
spring.thymeleaf.cache=false
1.6 新建登录页面和各自登录成功的页面
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
<base th:href="${#request.getContextPath()+'/'}">
</head>
<body>
User登录
<form action="/userLogin" method="post">
姓名: <input type="text" name="name" /><br />
密码: <input type="text" name="pwd" /><br />
<input type="submit" value="登录">
</form>
<br />
Admin登录
<form action="/adminLogin" method="post">
姓名: <input type="text" name="name" /><br />
密码: <input type="text" name="pwd" /><br />
<input type="submit" value="登录">
</form>
</body>
</html>
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
<base th:href="${#request.getContextPath()+'/'}">
</head>
<body>
this is user_index page
</body>
</html>
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
<base th:href="${#request.getContextPath()+'/'}">
</head>
<body>
this is admin_index page.
</body>
</html>
1.7 定义一个ShiroConfig
package com.wpj.config;
import com.wpj.authticator.CustomModularRealmAuthenticator;
import com.wpj.realm.AdminRealm;
import com.wpj.realm.UserRealm;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.context.annotation.Bean;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@SpringBootConfiguration
public class ShiroConfig {
// 拦截过滤器
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("securityManager") DefaultWebSecurityManager securityManager){
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
// 认证失败跳转页面
shiroFilterFactoryBean.setLoginUrl("/toLogin");
// 权限不够跳转页面
shiroFilterFactoryBean.setUnauthorizedUrl("/toUnAuthorization");
Map<String, String> map = new LinkedHashMap<String, String>();
map.put("/toLogin", "anon");
map.put("/userLogin", "anon");
map.put("/adminLogin", "anon");
map.put("/**", "authc");
shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
//设置安全管理器
shiroFilterFactoryBean.setSecurityManager(securityManager);
return shiroFilterFactoryBean;
}
// 配置安全管理器
@Bean
public DefaultWebSecurityManager securityManager(){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
// 设置校验的Realm对象
securityManager.setAuthenticator(authenticator());
List<Realm> realms = new ArrayList<Realm>();
realms.add(userRealm());
realms.add(adminRealm());
securityManager.setRealms(realms);
return securityManager;
}
@Bean
public UserRealm userRealm(){
UserRealm userRealm = new UserRealm();
return userRealm;
}
@Bean
public AdminRealm adminRealm(){
AdminRealm adminRealm = new AdminRealm();
return adminRealm;
}
// 认证器
@Bean
public CustomModularRealmAuthenticator authenticator(){
CustomModularRealmAuthenticator authenticator = new CustomModularRealmAuthenticator();
return authenticator;
}
}
1.8 定义User类和Admin类
package com.wpj.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User implements Serializable {
private static final long serialVersionUID = 8434196542261610760L;
private Integer id;
private String name;
private String pwd;
}
package com.wpj.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Admin implements Serializable {
private static final long serialVersionUID = -7243842037245054988L;
private Integer id;
private String name;
private String pwd;
}
1.9 定义UserController和AdminController和跳转到登录页面的Controller
package com.wpj.controller;
import com.wpj.pojo.User;
import com.wpj.token.CustomToken;
import com.wpj.type.LoginType;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class UserController {
private final String LOGIN_TYPE = LoginType.USER.toString();
private Logger logger = LoggerFactory.getLogger(UserController.class);
/**
* 用户登录的方法
* @return
*/
@RequestMapping("/userLogin")
public String login(User user){
// 封装请求对象
CustomToken customToken = new CustomToken(user.getName(),user.getPwd(), LOGIN_TYPE);
// 获取登录主体
Subject subject = SecurityUtils.getSubject();
// 登录
try {
subject.login(customToken);
if(subject.isAuthenticated()){
// 认证成功
return "user_index";
}
} catch (UnknownAccountException e) {
logger.error("未知账户");
} catch (IncorrectCredentialsException e) {
logger.error("密码错误");
} catch (Exception e) {
logger.error("其他错误");
}
return "login";
}
}
package com.wpj.controller;
import com.wpj.pojo.Admin;
import com.wpj.pojo.User;
import com.wpj.token.CustomToken;
import com.wpj.type.LoginType;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.subject.Subject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class AdminController {
private final String LOGIN_TYPE = LoginType.ADMIN.toString();
private Logger logger = LoggerFactory.getLogger(AdminController.class);
/**
* 管理员登录的方法
* @return
*/
@RequestMapping("/adminLogin")
public String login(Admin admin){
// 封装请求对象
CustomToken customToken = new CustomToken(admin.getName(),admin.getPwd(), LOGIN_TYPE);
// 获取登录主体
Subject subject = SecurityUtils.getSubject();
// 登录
try {
subject.login(customToken);
if(subject.isAuthenticated()){
// 认证成功
return "admin_index";
}
} catch (UnknownAccountException e) {
logger.error("未知账户");
} catch (IncorrectCredentialsException e) {
logger.error("密码错误");
} catch (Exception e) {
logger.error("其他错误");
}
return "login";
}
}
package com.wpj.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class IndexController {
@RequestMapping("/toLogin")
public String toLogin(){
return "login";
}
}
1.10 定义2个Realm
package com.wpj.realm;
import com.wpj.controller.UserController;
import com.wpj.pojo.User;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class UserRealm extends AuthorizingRealm {
private Logger logger = LoggerFactory.getLogger(UserRealm.class);
@Override
public String getName() {
return "UserRealm";
}
// 认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
// 获取用户名
String name = (String) authenticationToken.getPrincipal();
// 查询数据库
// 模拟。。。
//
if (!(name.equals("jiekami"))) {
return null;
}
logger.info("这里是User认证");
User user = new User(1,"jiekami","123");
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(user.getName(), user.getPwd(), getName());
return simpleAuthenticationInfo;
}
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
}
package com.wpj.realm;
import com.wpj.pojo.Admin;
import com.wpj.pojo.User;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class AdminRealm extends AuthorizingRealm {
private Logger logger = LoggerFactory.getLogger(AdminRealm.class);
@Override
public String getName() {
return "AdminRealm";
}
// 认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
// 获取用户名
String name = (String) authenticationToken.getPrincipal();
// 查询数据库
// 模拟。。。
//
if (!(name.equals("wpj"))) {
return null;
}
//
logger.info("这里是Admin认证");
Admin admin = new Admin(1,"wpj","123");
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(admin.getName(), admin.getPwd(), getName());
return simpleAuthenticationInfo;
}
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
return null;
}
}
1.11 启动主启动类
package com.wpj;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class ShiroManyrolesApplication {
public static void main(String[] args) {
SpringApplication.run(ShiroManyrolesApplication.class, args);
}
}
1.12 测试
localhost:8080
输入各自Realm模拟的数据即可跳转到各自的页面