本文共 9694 字,大约阅读时间需要 32 分钟。
代码
package com.springboot.in.action.daoimport java.util.Listimport com.springboot.in.action.entity.Userimport org.springframework.data.jpa.repository.Queryimport org.springframework.data.repository.CrudRepositoryimport scala.language.implicitConversionstrait UserDao extends CrudRepository[User, Integer] { def findAll(): List[User] // JavaConversions def save(u: User): User def findOne(id: Integer): User @Query(value = "select * from `user` where `username` = 1? limit 1", nativeQuery = true) def findByUsername(username: String): User}
报错日志
Caused by: org.springframework.dao.InvalidDataAccessResourceUsageException: could not extract ResultSet; SQL [n/a]; nested exception is org.hibernate.exception.SQLGrammarException: could not extract ResultSet at org.springframework.orm.jpa.vendor.HibernateJpaDialect.convertHibernateAccessException(HibernateJpaDialect.java:261) at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:244) at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:488) at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:59) at org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:213) at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:147) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:133) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179) at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:213) at com.sun.proxy.$Proxy97.findByUsername(Unknown Source) at com.springboot.in.action.service.LightSwordUserDetailService.loadUserByUsername(LightSwordUserDetailService.scala:25) at org.springframework.security.authentication.dao.DaoAuthenticationProvider.retrieveUser(DaoAuthenticationProvider.java:114) ... 54 common frames omittedCaused by: org.hibernate.exception.SQLGrammarException: could not extract ResultSet at org.hibernate.exception.internal.SQLExceptionTypeDelegate.convert(SQLExceptionTypeDelegate.java:63) at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:42) at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:109) at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:95) at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.extract(ResultSetReturnImpl.java:79) at org.hibernate.loader.Loader.getResultSet(Loader.java:2117) at org.hibernate.loader.Loader.executeQueryStatement(Loader.java:1900) at org.hibernate.loader.Loader.executeQueryStatement(Loader.java:1876) at org.hibernate.loader.Loader.doQuery(Loader.java:919) at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:336) at org.hibernate.loader.Loader.doList(Loader.java:2617) at org.hibernate.loader.Loader.doList(Loader.java:2600) at org.hibernate.loader.Loader.listIgnoreQueryCache(Loader.java:2429) at org.hibernate.loader.Loader.list(Loader.java:2424) at org.hibernate.loader.custom.CustomLoader.list(CustomLoader.java:336) at org.hibernate.internal.SessionImpl.listCustomQuery(SessionImpl.java:1967) at org.hibernate.internal.AbstractSessionImpl.list(AbstractSessionImpl.java:322) at org.hibernate.internal.SQLQueryImpl.list(SQLQueryImpl.java:125) at org.hibernate.jpa.internal.QueryImpl.list(QueryImpl.java:606) at org.hibernate.jpa.internal.QueryImpl.getSingleResult(QueryImpl.java:529) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:497).....Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''jack'' at line 1 at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62) at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) at java.lang.reflect.Constructor.newInstance(Constructor.java:422) at com.mysql.jdbc.Util.handleNewInstance(Util.java:425) at com.mysql.jdbc.Util.getInstance(Util.java:408) at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:943) at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3973) at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3909) at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2527) at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2680) at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2501) at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:1858) at com.mysql.jdbc.PreparedStatement.executeQuery(PreparedStatement.java:1966) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:497) at org.apache.tomcat.jdbc.pool.StatementFacade$StatementProxy.invoke(StatementFacade.java:138) at com.sun.proxy.$Proxy100.executeQuery(Unknown Source) at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.extract(ResultSetReturnImpl.java:70) ... 98 common frames omitted
sql语法手误。1?这地方写错了,应该是?1。这在敲代码的时候,手速一旦稍有不慎,就会导致前后顺序颠倒,而导致输入错误。这个虽然说是“低级错误”,但是错误搞起来,确实有时候很难想到这个你从来不会认为出错的地方。
@Query(value = "select * from `user` where `username` = 1? limit 1", nativeQuery = true) def findByUsername(username: String): User
@Query(value = "select * from `user` where `username` = ?1 limit 1", nativeQuery = true) def findByUsername(username: String): User
日志打出来的ROLE是USER,代码里调用的是@PreAuthorize("hasRole('USER')"),为什么权限却是不对?
后台打印日志:
username is jack, USER
LoginFilter:{ "accountNonExpired":true, "accountNonLocked":true, "authorities":[{ "authority":"USER" }], "credentialsNonExpired":true, "enabled":true, "username":"jack" }调用代码:
@RestController@RequestMapping(Array("/httpapi"))class HttpApiController @Autowired()( val HttpSuiteDao: HttpSuiteDao, val HttpApiDao: HttpApiDao, val HttpReportDao: HttpReportDao) { @PreAuthorize("hasRole('USER')") @RequestMapping(value = { Array("", "/") }, method = Array(RequestMethod.GET)) def list(model: Model) = { model.addAttribute("httpapis", HttpApiDao.findAll()) new ModelAndView("/httpapi/list") }....}
数据库存的是USER:
package com.springboot.in.action.serviceimport javax.annotation.PostConstructimport com.springboot.in.action.dao.{RoleDao, UserDao, UserRoleDao}import com.springboot.in.action.entity.{Role, User, UserRole}import org.springframework.beans.factory.annotation.Autowiredimport org.springframework.stereotype.Service/** * Created by jack on 2017/4/29. * 初始化测试数据 *///@Service // 需要初始化数据时,打开注释即可。class DataInit @Autowired()(val userDao: UserDao, val userRoleDao: UserRoleDao, val roleDao: RoleDao) { @PostConstruct def dataInit(): Unit = { val admin = new User val jack = new User admin.username = "admin" admin.password = "admin" jack.username = "jack" jack.password = "123456" userDao.save(admin) userDao.save(jack) val adminRole = new Role val userRole = new Role adminRole.role = "ADMIN" userRole.role = "USER" roleDao.save(adminRole) roleDao.save(userRole) val userRoleAdminRecord1 = new UserRole userRoleAdminRecord1.userId = admin.id userRoleAdminRecord1.roleId = adminRole.id userRoleDao.save(userRoleAdminRecord1) val userRoleAdminRecord2 = new UserRole userRoleAdminRecord2.userId = admin.id userRoleAdminRecord2.roleId = userRole.id userRoleDao.save(userRoleAdminRecord2) val userRoleJackRecord = new UserRole userRoleJackRecord.userId = jack.id userRoleJackRecord.roleId = userRole.id userRoleDao.save(userRoleJackRecord) }}
Spring Security默认前缀ROLE_问题。这个应该是框架的一个小缺陷。总感觉这样的一个潜规则在这里有点不大优雅。
数据库里面存的role角色要加上默认前缀:ROLE_
adminRole.role = "ROLE_ADMIN" userRole.role = "ROLE_USER"
这样改完之后,代码调用的地方保持不变,数据库里面角色必须统一有ROLE_前缀。而我们看到的后台打印的日志内容也是数据库的信息:
username is jack, ROLE_USERLoginFilter:{ "accountNonExpired":true, "accountNonLocked":true, "authorities":[{ "authority":"ROLE_USER" }], "credentialsNonExpired":true, "enabled":true, "username":"jack"}
这个小坑,估计很多初次学习使用Security框架的人都会踩到。所以,记个问题,以供参考。
转载地址:http://dtgfl.baihongyu.com/