先上项目结构图
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.4.1</version>
</dependency>
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.0.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.13</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
创建shiroConfig
这个类主要就是配置一些登录,无权限,开放接口和数据库权限表读取
@Configuration
public class ShiroConfig {
@Autowired
private MyRealm realm;
@Autowired
private RoleMapper roleMapper;
@Bean
public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager){
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
//设置登录跳转路径
shiroFilterFactoryBean.setLoginUrl("/login");
//设置无权限跳转路径
shiroFilterFactoryBean.setUnauthorizedUrl("/unauthorized");
Map<String,String> map = new HashMap<>();
//设置首页需要登录访问
map.put("/index","authc");
//查询数据库权限表获取权限列表
List<Role> roleList = roleMapper.findRoleList();
//将权限列表组装
roleList.forEach((t)->{
map.put(t.getPermissonurl(),"roles["+t.getRole()+"]");
});
shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
shiroFilterFactoryBean.setSecurityManager(securityManager);
return shiroFilterFactoryBean;
}
@Bean
public SecurityManager securityManager(){
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
//配置自己的Realm
securityManager.setRealm(realm);
return securityManager;
}
}
MyRealm,主要进行用户和权限验证
@Component
public class MyRealm extends AuthorizingRealm {
@Autowired
private UserMapper userMapper;
/**
* 验证角色权限信息
* @param principalCollection
* @return
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//获取到用户名
String userName = (String) principalCollection.getPrimaryPrincipal();
//根据用户名查询用户
User user = userMapper.findByUserName(userName);
if (user == null){
return null;
}
SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
//拿到用户role字段
String role = user.getRole();
if (StringUtils.isEmpty(role)){
return null;
}
//将用户权限进行组装
HashSet<String> set = new HashSet<>();
String[] split = role.split(",");
for (int i = 0; i < split.length; i++) {
set.add(split[i]);
}
authorizationInfo.setRoles(set);
return authorizationInfo;
}
/**
* 验证用户名密码
* @param token
* @return
* @throws AuthenticationException
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
//从token中拿到用户名
String userName = (String) token.getPrincipal();
if (userName == null){
return null;
}
//查询用户信息
User user = userMapper.findByUserName(userName);
if (user == null){
return null;
}
//传入密码进行验证
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(userName,user.getPassword(),user.getRealname());
return simpleAuthenticationInfo;
}
}
MD5工具类
public class MD5Util {
/**
* 加点盐
*/
private static final String SALT = "tom";
public static String encode(String password){
password = password + SALT;
MessageDigest md5 = null;
try {
md5 = MessageDigest.getInstance("MD5");
}catch (Exception e){
throw new RuntimeException();
}
char[] charArray = password.toCharArray();
byte[] byteArray = new byte[charArray.length];
for (int i = 0; i < charArray.length; i++)
byteArray[i] = (byte) charArray[i];
byte[] md5Bytes = md5.digest(byteArray);
StringBuffer hexValue = new StringBuffer();
for (int i = 0; i < md5Bytes.length; i++) {
int val = ((int) md5Bytes[i]) & 0xff;
if (val < 16){
hexValue.append("0");
}
hexValue.append(Integer.toHexString(val));
}
return hexValue.toString();
}
public static void main(String[] args) {
String encode = encode("123456");
System.out.println(encode);
}
}
登录controller
@Controller
public class LoginController {
@RequestMapping("/login")
public String login(){
return "login";
}
@RequestMapping("/userLogin")
public String userLogin(User user, HttpServletRequest request){
//获取主题用户
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(user.getUsername(), MD5Util.encode(user.getPassword()));
try{
//调用登录接口,该接口会调用MyRealm中的doGetAuthenticationInfo方法进行用户名密码验证,如果用户名密码比对错误会抛出异常
subject.login(usernamePasswordToken);
request.setAttribute("username",user.getUsername());
return "index";
}catch (Exception e){
e.printStackTrace();
request.setAttribute("error","账号或密码错误");
return "login";
}
}
}
其他controller都是普通接口,直接返回字符串,这里就贴一个
@RestController
@RequestMapping("member")
public class MemberController {
/**
* insert
* @return
*/
@RequestMapping("/insert")
public String index(){
return "insert";
}
/**
* update
* @return
*/
@ResponseBody
@RequestMapping("/update")
public String open(){
return "update";
}
}
数据库表按最简单设计
role表
user表
再贴一个mapper类做参考
public interface UserMapper {
@Select("select id,username,password,realname,role"
+ " from user where username=#{userName}")
User findByUserName(@Param("userName") String userName);
}
yml文件
spring:
datasource:
url: jdbc:mysql://ip:port/shiro?useUnicode=true&&characterEncoding=utf-8&serverTimezone=GMT
username: root
password: ****
type: com.alibaba.druid.pool.DruidDataSource
thymeleaf:
cache: false
prefix: classpath:/templates/
suffix: .ftl
mybatis:
mapper-locations: classpath:mapping/*.xml
type-aliases-package: cn.xej.pojo
configuration:
map-underscore-to-camel-case: true # 该配置就是将带有下划线的表字段映射为驼峰格式的实体类属性
整体流程就是:
启动项目会经过ShiroConfig的securityManager,先加载自己的Realm
然后走到ShiroConfig的shiroFilter方法加载我们自己配置的登录,无权限,开放接口和数据库权限配置
1.用户首次访问首页跳转到登录页面
2.点击登录先进入userLogin组装token
3.调用subject的login方法被MyRealm的doGetAuthenticationInfo方法拦截验证登录
4.登录成功则跳转至首页,否则跳转至登录页面回到1
5.登录成功后访问其他需要认证和权限的请求会访问到MyRealm的doGetAuthorizationInfo方法进行用户角色验证,验证通过则正常访问,不通过则跳转至无权限配置的请求
本次分享到这里就结束了,大家可以在此基础上研究更加细化的权限控制缓存等,也就是五张表