这里同时导入了log4j, Shiro默认的日志是commons-logging
<dependencies>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>1.6.0</version>
</dependency>
<!-- configure logging -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<scope>runtime</scope>
<version>1.7.21</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<scope>runtime</scope>
<version>1.7.21</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<scope>runtime</scope>
<version>1.2.17</version>
</dependency>
</dependencies>
log4j.rootLogger=INFO, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m %n
# General Apache libraries
log4j.logger.org.apache=WARN
# Spring
log4j.logger.org.springframework=WARN
# Default Shiro logging
log4j.logger.org.apache.shiro=INFO
# Disable verbose logging
log4j.logger.org.apache.shiro.util.ThreadContext=WARN
log4j.logger.org.apache.shiro.cache.ehcache.EhCache=WARN
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
#
# =============================================================================
# Quickstart INI Realm configuration
#
# For those that might not understand the references in this file, the
# definitions are all based on the classic Mel Brooks‘ film "Spaceballs". ;)
# =============================================================================
# -----------------------------------------------------------------------------
# Users and their assigned roles
#
# Each line conforms to the format defined in the
# org.apache.shiro.realm.text.TextConfigurationRealm#setUserDefinitions JavaDoc
# -----------------------------------------------------------------------------
[users]
# user ‘root‘ with password ‘secret‘ and the ‘admin‘ role
root = secret, admin
# user ‘guest‘ with the password ‘guest‘ and the ‘guest‘ role
guest = guest, guest
# user ‘presidentskroob‘ with password ‘12345‘ ("That‘s the same combination on
# my luggage!!!" ;)), and role ‘president‘
presidentskroob = 12345, president
# user ‘darkhelmet‘ with password ‘ludicrousspeed‘ and roles ‘darklord‘ and ‘schwartz‘
darkhelmet = ludicrousspeed, darklord, schwartz
# user ‘lonestarr‘ with password ‘vespa‘ and roles ‘goodguy‘ and ‘schwartz‘
lonestarr = vespa, goodguy, schwartz
# -----------------------------------------------------------------------------
# Roles with assigned permissions
#
# Each line conforms to the format defined in the
# org.apache.shiro.realm.text.TextConfigurationRealm#setRoleDefinitions JavaDoc
# -----------------------------------------------------------------------------
[roles]
# ‘admin‘ role has all permissions, indicated by the wildcard ‘*‘
admin = *
# The ‘schwartz‘ role can do anything (*) with any lightsaber:
schwartz = lightsaber:*
# The ‘goodguy‘ role is allowed to ‘drive‘ (action) the winnebago (type) with
# license plate ‘eagle5‘ (instance specific id)
goodguy = winnebago:drive:eagle5
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.Factory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Quickstart {
private static final transient Logger log = LoggerFactory.getLogger(Quickstart.class);
public static void main(String[] args) {
// The easiest way to create a Shiro SecurityManager with configured
// realms, users, roles and permissions is to use the simple INI config.
// We‘ll do that by using a factory that can ingest a .ini file and
// return a SecurityManager instance:
// Use the shiro.ini file at the root of the classpath
// (file: and url: prefixes load from files and urls respectively):
Factory<SecurityManager> factory = new IniSecurityManagerFactory("classpath:shiro.ini");
SecurityManager securityManager = factory.getInstance();
// for this simple example quickstart, make the SecurityManager
// accessible as a JVM singleton. Most applications wouldn‘t do this
// and instead rely on their container configuration or web.xml for
// webapps. That is outside the scope of this simple quickstart, so
// we‘ll just do the bare minimum so you can continue to get a feel
// for things.
SecurityUtils.setSecurityManager(securityManager);
// Now that a simple Shiro environment is set up, let‘s see what you can do:
// get the currently executing user:
//获取当前的用户对象 Subject
Subject currentUser = SecurityUtils.getSubject();
// Do some stuff with a Session (no need for a web or EJB container!!!)
//通过当前用户拿到 session (可以脱离Web)
Session session = currentUser.getSession();
session.setAttribute("someKey", "aValue");
String value = (String) session.getAttribute("someKey");
if (value.equals("aValue")) {
log.info("Retrieved the correct value! [" + value + "]");
}
// let‘s login the current user so we can check against roles and permissions:
//判断当前的用户是否被认证
if (!currentUser.isAuthenticated()) {
//Token : 令牌, 此处没有获取, 随机设置
UsernamePasswordToken token = new UsernamePasswordToken("lonestarr", "vespa");
//设置记住我
token.setRememberMe(true);
try {
//执行登录操作
currentUser.login(token);
} catch (UnknownAccountException uae) {
log.info("There is no user with username of " + token.getPrincipal());
} catch (IncorrectCredentialsException ice) {
log.info("Password for account " + token.getPrincipal() + " was incorrect!");
} catch (LockedAccountException lae) {
log.info("The account for username " + token.getPrincipal() + " is locked. " +
"Please contact your administrator to unlock it.");
}
// ... catch more exceptions here (maybe custom ones specific to your application?
//认证异常(总的异常)
catch (AuthenticationException ae) {
//unexpected condition? error?
}
}
//say who they are:
//print their identifying principal (in this case, a username):
log.info("User [" + currentUser.getPrincipal() + "] logged in successfully.");
//test a role:
if (currentUser.hasRole("schwartz")) {
log.info("May the Schwartz be with you!");
} else {
log.info("Hello, mere mortal.");
}
//test a typed permission (not instance-level)
if (currentUser.isPermitted("lightsaber:wield")) {
log.info("You may use a lightsaber ring. Use it wisely.");
} else {
log.info("Sorry, lightsaber rings are for schwartz masters only.");
}
//a (very powerful) Instance Level permission:
if (currentUser.isPermitted("winnebago:drive:eagle5")) {
log.info("You are permitted to ‘drive‘ the winnebago with license plate (id) ‘eagle5‘. " +
"Here are the keys - have fun!");
} else {
log.info("Sorry, you aren‘t allowed to drive the ‘eagle5‘ winnebago!");
}
//all done - log out!
//注销
currentUser.logout();
//结束启动
System.exit(0);
}
}
Shiro三大核心
<!--shiro整合包-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-web-starter</artifactId>
<version>1.6.0</version>
</dependency>
package com.wang.config;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
//自定义的 UserRealm, 继承AuthorizingRealm即可
public class UserRealm extends AuthorizingRealm {
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("执行了 => AuthorizationInfo 授权");
return null;
}
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("执行了 => AuthenticationInfo 认证");
return null;
}
}
注意
package com.wang.config;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
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 java.util.LinkedHashMap;
@Configuration
public class ShiroConfig {
//ShiroFilterFactoryBean : 3
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("getDefaultWebSecurityManager") DefaultWebSecurityManager defaultWebSecurityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
//关联SecurityManager, 设置安全管理器
shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);
//添加shiro的内置过滤器
/*
anon: 无需认证, 就可以访问
authc: 必须认证了才能访问
user: 必须拥有 记住我 功能才能访问
perms: 拥有对于某个资源的权限才能访问
role: 拥有某个角色权限才能访问
*/
LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
// filterChainDefinitionMap.put("/user/add", "authc");
// filterChainDefinitionMap.put("/user/update", "authc");
filterChainDefinitionMap.put("/user/*", "authc");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
//设置登录的请求
shiroFilterFactoryBean.setLoginUrl("/toLogin");
return shiroFilterFactoryBean;
}
//DefaultWebSecurityManager : 2
//@Qualifier() 利用bean的id注入, 在注解托管中即为方法名
@Bean
public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm) {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
//关联UserRealm
securityManager.setRealm(userRealm);
return securityManager;
}
//创建realm对象, 需要自定义 : 1
//将自己定义的Realm注册为Bean, 被SpringBoot托管
@Bean
public UserRealm userRealm() {
return new UserRealm();
}
}
注意:
anon: 无需认证, 就可以访问
authc: 必须认证了才能访问
user: 必须拥有 记住我 功能才能访问
perms: 拥有对于某个资源的权限才能访问
role: 拥有某个角色权限才能访问
package com.wang.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class MyController {
@RequestMapping({"/", "/index"})
public String toIndex(Model model) {
model.addAttribute("msg", "Hello, Shiro!");
return "index";
}
@RequestMapping("/user/add")
public String add() {
return "user/add";
}
@RequestMapping("/user/update")
public String update() {
return "user/update";
}
@RequestMapping("/toLogin")
public String toLogin() {
return "login";
}
}
前端页面代码略
这里写在controller中, 利用不同的结果输出提示信息并进行跳转
@RequestMapping("/login")
public String login(String username, String password, Model model) {
//获取当前的用户
Subject subject = SecurityUtils.getSubject();
//封装用户的登录数据
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
//执行登录的方法, 如果没有异常, 说明登录OK
try {
subject.login(token);
return "index";
} catch (UnknownAccountException e) {
//用户名不存在
model.addAttribute("msg", "用户名错误");
return "login";
} catch (IncorrectCredentialsException e) {
//密码不存在
model.addAttribute("msg", "密码错误");
return "login";
}
注意
在自定义的UserRealm中, 进行配置
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("执行了 => AuthenticationInfo 认证");
//用户名, 密码 ==> 数据库中取
String name = "root";
String password = "123456";
UsernamePasswordToken userToken = (UsernamePasswordToken) authenticationToken;
//用户名认证
if (!userToken.getUsername().equals(name)) {
//抛出异常 UnknownAccountException
return null;
}
//密码认证 : shiro做
return new SimpleAuthenticationInfo("", password, "");
}
注意
Shiro不需要导入与Mybatis的整合, 只需要导入与thymeleaf的整合包
<!--shiro整合thymeleaf-->
<!-- https://mvnrepository.com/artifact/com.github.theborakompanioni/thymeleaf-extras-shiro -->
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.0.0</version>
</dependency>
package com.wang.config;
import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
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 java.util.LinkedHashMap;
@Configuration
public class ShiroConfig {
//ShiroFilterFactoryBean : 3
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("getDefaultWebSecurityManager") DefaultWebSecurityManager defaultWebSecurityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
//关联SecurityManager, 设置安全管理器
shiroFilterFactoryBean.setSecurityManager(defaultWebSecurityManager);
//添加shiro的内置过滤器
/*
anon: 无需认证, 就可以访问
authc: 必须认证了才能访问
user: 必须拥有 记住我 功能才能访问
perms: 拥有对于某个资源的权限才能访问
role: 拥有某个角色权限才能访问
*/
//拦截
LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
// filterChainDefinitionMap.put("/user/add", "authc");
// filterChainDefinitionMap.put("/user/update", "authc");
//授权
filterChainDefinitionMap.put("/user/add", "perms[user:add]");
filterChainDefinitionMap.put("/user/update", "perms[user:update]");
filterChainDefinitionMap.put("/user/*", "authc");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
//设置登录的请求
shiroFilterFactoryBean.setLoginUrl("/toLogin");
//未授权页面
shiroFilterFactoryBean.setUnauthorizedUrl("/noauth");
return shiroFilterFactoryBean;
}
//DefaultWebSecurityManager : 2
//@Qualifier() 利用bean的id注入, 在注解托管中即为方法名
@Bean
public DefaultWebSecurityManager getDefaultWebSecurityManager(@Qualifier("userRealm") UserRealm userRealm) {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
//关联UserRealm
securityManager.setRealm(userRealm);
return securityManager;
}
//创建realm对象, 需要自定义 : 1
//将自己定义的Realm注册为Bean, 被SpringBoot托管
@Bean
public UserRealm userRealm() {
return new UserRealm();
}
//整合ShiroDialect: 用来整合 shiro thymeleaf
@Bean
public ShiroDialect shiroDialect() {
return new ShiroDialect();
}
}
注意
package com.wang.config;
import com.wang.pojo.User;
import com.wang.service.UserServiceImpl;
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.session.Session;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
//自定义的 UserRealm, 继承AuthorizingRealm即可
public class UserRealm extends AuthorizingRealm {
@Autowired
private UserServiceImpl userService;
//授权
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
System.out.println("执行了 => AuthorizationInfo 授权");
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
//拿到当前登录的这个对象, user在下面已经放到了subject中
Subject subject = SecurityUtils.getSubject();
//拿到user对象
User currentUser = (User) subject.getPrincipal();
//放到perms中, 在ShiroConfig中以K-V调用
info.addStringPermission(currentUser.getPerms());
return info;
}
//认证
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
System.out.println("执行了 => AuthenticationInfo 认证");
//用户名, 密码 ==> 数据库中取
//从token中获得用户名
UsernamePasswordToken userToken = (UsernamePasswordToken) authenticationToken;
String username = userToken.getUsername();
User user = userService.queryUserByName(username);
//输入的用户名在数据库中不存在
if (user == null) {
return null;
}
Subject currentSubject = SecurityUtils.getSubject();
Session session = currentSubject.getSession();
session.setAttribute("loginUser", user);
String password = user.getPwd();
//密码认证 : shiro做(password与token从前端取出的密码进行比较)
//此处传入一个user, 放到了Subject中
return new SimpleAuthenticationInfo(user, password, "");
}
}
注意
授权
认证
用户名和密码已经放在了token中 UsernamePasswordToken userToken = (UsernamePasswordToken) authenticationToken; 这个token是从controller层中利用从前端的数据封装好的
这里可以设置Subject对象的session, 存放一个loginUser字段, 判断是否有User从而选择是否在前端显示登陆的链接, 这里的session不是Web中的session, 是Shiro自带的, 全局都能用(和web的session差不多)
SimpleAuthenticationInfo对象同样的是返回我们设置完的认证对象, 参数设置如下
principal the ‘primary‘ principal associated with the specified realm.
credentials the credentials that verify the given principal.
realmName the realm from where the principal and credentials were acquired.
public SimpleAuthenticationInfo(Object principal, Object credentials, String realmName)
此处的用户名, 密码, 以及用户对象都从数据库中取出
此处的放回值 return new SimpleAuthenticationInfo(user, password, ""); 里面传递了我们从数据库中取出的user, 在授权时由于关联了字段, 因此Shiro会帮我们从user中取出perms对应的字段, 和config中的过滤器进行比较
@RequestMapping("/login")
public String login(String username, String password, Model model) {
//获取当前的用户
Subject subject = SecurityUtils.getSubject();
//封装用户的登录数据
UsernamePasswordToken token = new UsernamePasswordToken(username, password);
//执行登录的方法, 如果没有异常, 说明登录OK
try {
subject.login(token);
return "index";
} catch (UnknownAccountException e) {
//用户名不存在
model.addAttribute("msg", "用户名错误");
return "login";
} catch (IncorrectCredentialsException e) {
//密码不存在
model.addAttribute("msg", "密码错误");
return "login";
}
}
注意
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"
xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>首页</h1>
<p>
<span th:text="${msg}"></span>
</p>
<p th:if="${session.loginUser==null}">
<a th:href="@{/toLogin}">登录</a>
</p>
<hr>
<div shiro:hasPermission="user:add">
<a th:href="@{/user/add}">add</a>
</div>
<div shiro:hasPermission="user:update">
<a th:href="@{/user/update}">update</a>
</div>
</body>
</html>
注意
原文:https://www.cnblogs.com/wang-sky/p/13728638.html