Secret Garden: https://smithcruise.coding.net/public/
smith-cruise / spring-boot-shiro Goto Github PK
View Code? Open in Web Editor NEWShiro基于SpringBoot +JWT搭建简单的restful服务
Shiro基于SpringBoot +JWT搭建简单的restful服务
Secret Garden: https://smithcruise.coding.net/public/
老铁, 能不能集成jwt的 refreshToken呢 , 现在过期时间是五分钟时间短, 就算延长了1小时的话, 那如果过期了的话, 用户还是要重新登录, 能不能通过不重新登录的方式, 而重新获取一个token呢 ?
否则client这边需要支持http 302 redirect才能返回正常的错误。
就算能redirect,异常的错误信息也丢失了
关于项目本身:
1. 个人理解shiro的cache主要用在session方面,既然使用了JWT鉴权,似乎就没必要关心shiro的cache机制,至于验证鉴权以外其他方面的缓存需,完全可以直接使用 spring-boot-starter-cache
, 结合其他cache实现来做.
2.jwt 串放在Header的Authorization字段上的时候一般开头是Bearer
,隔一个空格后才是jwt串,这样区别于Basic
和Digest
非常感谢您的无私开源!
已反复阅读您项目的Readme.md
内容,非常感谢。
如果用户携带token放问我,我如何在Controller
中获取Token中的username,还是需要用户在提交时额外提交一个参数告诉我他的username呢?
ShiroRealm中抛出的异常throw new AuthenticationException ("登录凭证已过期,请重新登录!");
在ExceptionController中捕获到的ShiroException中之前抛出的AuthenticationException其Message 被改写了
executeLogin执行了两次 为什么?
Map<String, String> filterRuleMap = new HashMap<>();
// 访问401和404页面不通过我们的Filter
filterRuleMap.put("/401", "anon");
filterRuleMap.put("/login", "anon");
// 所有请求通过我们自己的JWT Filter
filterRuleMap.put("/**", "jwt");
factoryBean.setFilterChainDefinitionMap(filterRuleMap);
return factoryBean;
只要请求头里有Authorization就会去执行认证。
做的很棒,受益匪浅。请问 Refresh Token 怎么做呢?
在套用你的项目的时候,发现项目目录下不存在"Algorithm"这个文件
添加了跨域,是不是要给login请求添加白名单
在JWTFilter中有如下方法
@Override
protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
String authorization = httpServletRequest.getHeader("Authorization");
authorization = authorization.replace("Bearer ", "");
JWTToken token = new JWTToken(authorization);
// 提交给realm进行登入,如果错误他会抛出异常并被捕获
getSubject(request, response).login(token);
// 如果没有抛出异常则代表登入成功,返回true
return true;
}
此时Header中带有Authorization,那么将会去MyRealm中进行用户验证。
如果Authorization传过来的token是错误的,就会抛出异常。但是再MyRealm.java 中的doGetAuthenticationInfo中抛出的异常,无法被ExceptionController所捕获,就导致返回的数据结构不统一了。
请问有解决办法吗?
当你发起一个请求之后,进入到自定义的Filter里面判断是否登录,假如你的token是正确的就直接进入该方法了。不会进入到doGetAuthorizationInfo方法的权限验证里面。
难道是我打开的姿势不对?
你好,想问下,token过期后会自动登录然后把新的token返回吗?
使用url拼接登陆,获取token之后,获取不到权限。导致授权失败,无法访问权限页面
我想问下我执行的login方法JWTFilter里面的isLoginAttempt方法返回为啥为false,它的字面意思不是尝试登陆吗,
还有个executeLogin方法一直不会执行。(由于isLoginAttempt是false)我对这不是很理解。大佬指点一下
多太服务器,A服务获取的到token,在B服务器能用吗
git clone 代码,启动运行成功
通过修改ShiroConfig解决了:
@configuration
public class ShiroConfig {
@Bean
public SecurityManager securityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
// 设置realm.
securityManager.setRealm(myShiroRealm());
/*
* 关闭shiro自带的session,详情见文档
* http://shiro.apache.org/session-management.html#SessionManagement-StatelessApplications%28Sessionless%29
*/
DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO();
DefaultSessionStorageEvaluator defaultSessionStorageEvaluator = new DefaultSessionStorageEvaluator();
defaultSessionStorageEvaluator.setSessionStorageEnabled(false);
subjectDAO.setSessionStorageEvaluator(defaultSessionStorageEvaluator);
securityManager.setSubjectDAO(subjectDAO);
return securityManager;
}
@Bean
public MyRealm myShiroRealm() {
return new MyRealm();
}
@Bean
public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
ShiroFilterFactoryBean factoryBean = new ShiroFilterFactoryBean();
factoryBean.setSecurityManager(securityManager);
// shiro对于验证不通过的请求,会自动跳转到登录页,这里针对APP,设置为返回一个固定的
factoryBean.setLoginUrl("/600");
// 添加自己的过滤器并且取名为token,在下方使用
Map<String, Filter> filterMap = new HashMap<>();
filterMap.put("token", new TokenFilter());
factoryBean.setFilters(filterMap);
/*
* 自定义url规则
* http://shiro.apache.org/web.html#urls-
*/
Map<String, String> filterRuleMap = new LinkedHashMap<>();
// 配置不会被拦截的路径 顺序判断
filterRuleMap.put("/600", "anon");
filterRuleMap.put("/member/login", "anon");
// 所有请求通过我们自己的token Filter
filterRuleMap.put("/**", "token");
factoryBean.setFilterChainDefinitionMap(filterRuleMap);
return factoryBean;
}
/**
* 下面的代码是添加注解支持
*/
@Bean
@DependsOn("lifecycleBeanPostProcessor")
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
return new DefaultAdvisorAutoProxyCreator();
}
@Bean
public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
}
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
advisor.setSecurityManager(securityManager);
return advisor;
}
}
Shiro源码是执行isAccessAllowed和onAccessDenied方法,现在只重写了isAccessAllowed,而onAccessDenied源码是重复调用了executeLogin方法,造成了循环多次调用Shiro认证方法
在自定义 Filter
的 onPreHandle
方法中添加如下代码即可。
request.setAttribute(DefaultSubjectContext.SESSION_CREATION_ENABLED, Boolean.FALSE);
对于不需要认证的页面使用 NoSessionCreationFilter
而不是 anon
。
filterRuleMap.put("/401", "noSessionCreation");
具体可看官方文档 NoSessionCreationFilter 和源码
package org.apache.shiro.web.filter.session;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import org.apache.shiro.subject.support.DefaultSubjectContext;
import org.apache.shiro.web.filter.PathMatchingFilter;
public class NoSessionCreationFilter extends PathMatchingFilter {
public NoSessionCreationFilter() {
}
protected boolean onPreHandle(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
request.setAttribute(DefaultSubjectContext.SESSION_CREATION_ENABLED, Boolean.FALSE);
return true;
}
}
您好:
我想问一下,这个项目中没有使用到任何web.xml文件配置filter,也没有使用注入filter用到的FilterRegistrationBean 类,请教为什么可以做到 shiro能拦截到所有请求的,具体实现是在哪里呢,麻烦了~
在pom文件中加入以下依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.8.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.44</version>
</dependency>
返回全部变成404
{
"timestamp": 1519257821400,
"status": 404,
"error": "Not Found",
"message": "No message available",
"path": "/require_role"
}
后来发现把
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.8.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
去掉就会正常。
或者保留spring-boot-starter-parent,把spring-boot-starter-data-jpa去掉也会正常,但不知道为什么会这样???
你好,有一个问题,preHandle(ServletRequest request, ServletResponse response)
方法按照你的方式写好之后,执行Spring MVC 的@DeleteMapping
的方法会报错
Method DELETE is not allowed by Access-Control-Allow-Methods in preflight response.
但是如果我将这一句
httpResponse.setHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS, httpRequest.getMethod());
修改为下面的样子的话,就可以正常执行
httpResponse.setHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS, httpRequest.getMethod()+", DELETE");
但我不太清楚为什么
Application
@SpringBootApplication
@MapperScan(basePackages = {"org.inlighting.dao"})
@EnableTransactionManagement
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application .class);
}
}
Service
public interface IUserService {
UserBean getUser(String username) ;
}
ServiceImpl
@Service
public class UserService implements IUserService{
private final UserMapper mapper;
@Autowired
public UserService(UserMapper mapper) {
this.mapper = mapper;
}
@Transactional(rollbackFor = Exception.class)
public UserBean getUser(String username) {
// 没有此用户直接返回null
Map<String, String> detail = mapper.selectUserByName(username);
if (detail == null )
return null;
UserBean user = new UserBean();
user.setUsername(username);
user.setPassword(detail.get("USER_PSWD"));
user.setRole(detail.get("ROLE_NAME"));
user.setPermission("*");
return user;
}
}
log
2018-05-17 10:51:12.517 DEBUG 9948 --- [nio-1024-exec-1] org.mybatis.spring.SqlSessionUtils : Creating a new SqlSession
2018-05-17 10:51:12.520 DEBUG 9948 --- [nio-1024-exec-1] org.mybatis.spring.SqlSessionUtils : SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@57f320e4] was not registered for synchronization because synchronization is not active
2018-05-17 10:51:12.856 INFO 9948 --- [nio-1024-exec-1] com.alibaba.druid.pool.DruidDataSource : {dataSource-1} inited
2018-05-17 10:51:12.860 DEBUG 9948 --- [nio-1024-exec-1] o.i.dao.UserMapper.selectUserByName : ==> Preparing: select * from user where userName = ?
2018-05-17 10:51:12.882 DEBUG 9948 --- [nio-1024-exec-1] o.i.dao.UserMapper.selectUserByName : ==> Parameters: ricky(String)
2018-05-17 10:51:12.905 DEBUG 9948 --- [nio-1024-exec-1] o.i.dao.UserMapper.selectUserByName : <== Total: 1
2018-05-17 10:51:12.906 DEBUG 9948 --- [nio-1024-exec-1] org.mybatis.spring.SqlSessionUtils : Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@57f320e4]
删除 DefaultAdvisorAutoProxyCreator
配置就正常
2018-05-17 11:02:05.302 DEBUG 7380 --- [nio-1024-exec-1] org.mybatis.spring.SqlSessionUtils : Creating a new SqlSession
2018-05-17 11:02:05.307 DEBUG 7380 --- [nio-1024-exec-1] org.mybatis.spring.SqlSessionUtils : Registering transaction synchronization for SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@6c7ef00b]
2018-05-17 11:02:05.320 DEBUG 7380 --- [nio-1024-exec-1] o.i.dao.UserMapper.selectUserByName : ==> Preparing: SELECT * from user where userName = ?
2018-05-17 11:02:05.342 DEBUG 7380 --- [nio-1024-exec-1] o.i.dao.UserMapper.selectUserByName : ==> Parameters: ricky(String)
2018-05-17 11:02:05.365 DEBUG 7380 --- [nio-1024-exec-1] o.i.dao.UserMapper.selectUserByName : <== Total: 1
2018-05-17 11:02:05.366 DEBUG 7380 --- [nio-1024-exec-1] org.mybatis.spring.SqlSessionUtils : Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@6c7ef00b]
2018-05-17 11:02:05.366 DEBUG 7380 --- [nio-1024-exec-1] org.mybatis.spring.SqlSessionUtils : Transaction synchronization committing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@6c7ef00b]
2018-05-17 11:02:05.366 DEBUG 7380 --- [nio-1024-exec-1] org.mybatis.spring.SqlSessionUtils : Transaction synchronization deregistering SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@6c7ef00b]
2018-05-17 11:02:05.366 DEBUG 7380 --- [nio-1024-exec-1] org.mybatis.spring.SqlSessionUtils : Transaction synchronization closing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@6c7ef00b]
有其他的好方法吗?
This subject is anonymous - it does not have any identifying principals and " +
"authorization operations require an identity to check against. A Subject instance will " +
"acquire these identifying principals automatically after a successful login is performed " +
"be executing org.apache.shiro.subject.Subject.login(AuthenticationToken) or when 'Remember Me' " +
"functionality is enabled by the SecurityManager. This exception can also occur when a " +
"previously logged-in Subject has logged out which " +
"makes it anonymous again. Because an identity is currently not known due to any of these " +
"conditions, authorization is denied.
请问下shiro在认证并授权后返回的SimpleAuthorizationInfo在controller中只能通过SecurityUtils.getSubject().hasRole()去判断认证的用户是否有某个权限而不能拿到这些权限的具体内容对么?查了很多资料,好像都是这样简单地去判断 。
我现在想实现的是返回给前端当前用户的角色以及他能访问的菜单栏的数据(菜单名称,url等),前端vuejs遍历这些返回的数据并渲染出菜单,jwt的部分已经实现了。虽然后台也可以单独地去查数据库然后直接返回,但是这样好像就没用到shiro的realm,请问您知道这个怎么解决么或者这个思路是错了么?
在verify方法代码中从try开始往下第六行代码定义为何意?既然声明了,为何无返回或使用。
在控制器的其中一个方法加了权限判断注解,按我的理解是这个方法没有权限,访问就会受限,但是实际上这个控制器的其它方法也受到了同样的限制。请问这里的逻辑是怎样的
代码如下
@RestController
@RequestMapping("/users")
public class UserController {
@Autowired
private UserRepository userRepository;
@Autowired
private UserService userService;
@GetMapping
// @RequiresPermissions("user:index")
public ResponseContent index() {
return ResponseUtil.success(userRepository.findAll());
}
@PostMapping
@RequiresPermissions("user:store")
public ResponseContent store(@RequestParam String username, @RequestParam String password) {
User user = userService.createNewUser(username, password);
return ResponseUtil.success(user);
}
@DeleteMapping("/{id}")
// @RequiresPermissions("user:del")
public ResponseContent delete(@PathVariable long id) {
userRepository.delete(id);
return ResponseUtil.success();
}
}
访问get http://localhost:8080/users
会得到:
{
"timestamp": 1516766046036,
"status": 404,
"error": "Not Found",
"message": "No message available",
"path": "/users"
}
就是我发现,你的代码中登录时根本没有调用shiro的认证,那么你是怎么把角色和权限放到shiro中的呢
当拿token来请求的时候,存在token过期无效,token本身无效的情况,没想明白为什么要抛出User didn't existed! 和 Username or password error 的异常,两个异常是不是在登陆时抛出更好一些。
据我了解,JWT即使颁发新令牌也不会将旧令牌作废
不知道作者有什么思路可以分享
目前作者的DEMO 每当令牌过期后就要让用户重新登录了
@OverRide
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
这个方法为什么每次请求时会执行2次,不是应该只执行一次吗?
你好,请教下, 按这个结构,页面上超链接跳转,js中location.href跳转时怎么实现权限控制?header中怎么加Authorization字段?
能看出来作者确实用心在写文档,但我能力有限跑起来有问题
hashmap怎么模拟的?
{ "username": "smith", "password": "smith123", "role": "user", "permission": "view" }
返回:
{ "code": 500, "msg": "Required String parameter 'username' is not present", "data": null }
请教大佬一个问题,如果这里的secret换成一个服务器独有的密钥会不会好点?
因为调用者如果知道是这种签名方式,是不是能通过自己的密码来签名,然后通过设置很大的过期时间达到无限请求接口的目的?
var token;
$(".bu").click(function() {
$.post('http://localhost:8080/login', {
username: 'smith',
password: 'smith123'
}, function(data, textStatus, xhr) {
/*optional stuff to do after success */
console.log(data);
token = data.data;
});
});
$(".require_auth").click(function() {
$.get('http://localhost:8080/require_auth', {
Authorization: token
}, function(data, textStatus, xhr) {
/*optional stuff to do after success */
console.log(data);
});
});
@GetMapping("/require_auth")
@RequiresAuthentication
public ResponseBean requireAuth() {
return new ResponseBean(200, "You are authenticated", null);
}
作者大大这是为嘛呀,绝望脸,我那个token那个加错了的意思么
这个方法感觉没在shiro的机制中,如果直接访问该方法映射的请求,那么整个系统就相当于进行了两次登录操作(一次是该login本身登录逻辑,一次是shiro拦截器引起的登录逻辑),感觉怪怪的,不知道你的想法是啥
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.