SpringBoot+shiro+Swagger實現前後分離的框架
1、導包
< dependency>
< groupId> org.springframework.boot</ groupId>
< artifactId> spring-boot-starter-web</ artifactId>
</ dependency>
< dependency>
< groupId> org.projectlombok</ groupId>
< artifactId> lombok</ artifactId>
< optional> true</ optional>
</ dependency>
< dependency>
< groupId> com.alibaba</ groupId>
< artifactId> fastjson</ artifactId>
< version> 1.2.54</ version>
</ dependency>
< dependency>
< groupId> org.apache.shiro</ groupId>
< artifactId> shiro-spring</ artifactId>
< version> 1.4.0</ version>
</ dependency>
< dependency>
< groupId> org.springframework.boot</ groupId>
< artifactId> spring-boot-starter-jdbc</ artifactId>
</ dependency>
< dependency>
< groupId> org.mybatis.spring.boot</ groupId>
< artifactId> mybatis-spring-boot-starter</ artifactId>
< version> 2.1.1</ version>
</ dependency>
< dependency>
< groupId> mysql</ groupId>
< artifactId> mysql-connector-java</ artifactId>
</ dependency>
< dependency>
< groupId> com.alibaba</ groupId>
< artifactId> druid</ artifactId>
< version> 1.1.0</ version>
</ dependency>
< dependency>
< groupId> io.springfox</ groupId>
< artifactId> springfox-swagger2</ artifactId>
< version> 2.7.0</ version>
</ dependency>
< dependency>
< groupId> io.springfox</ groupId>
< artifactId> springfox-swagger-ui</ artifactId>
< version> 2.7.0</ version>
</ dependency>
2、封裝token
public class CustomToken extends UsernamePasswordToken {
private String token;
public CustomToken ( String token) {
this . token= token;
}
@Override
public Object getPrincipal ( ) {
return token;
}
}
3、編寫過濾器
public class CustomAccessControllerFilter extends AccessControlFilter {
@Override
protected boolean isAccessAllowed ( ServletRequest servletRequest, ServletResponse servletResponse, Object o) throws Exception {
return false ;
}
@Override
protected boolean onAccessDenied ( ServletRequest servletRequest, ServletResponse servletResponse) throws Exception {
HttpServletRequest request= ( HttpServletRequest) servletRequest;
try {
String token= request. getHeader ( Constant. REQ_TOKEN) ;
if ( StringUtils. isEmpty ( token) ) {
throw new BusinessException ( 400001 , "使用者的請求的token不能為空" ) ;
} else {
CustomToken customToken = new CustomToken ( token) ;
getSubject ( servletRequest, servletResponse) . login ( customToken) ;
}
} catch ( BusinessException e) {
resultResponse ( e. getMessageCode ( ) , e. getDefaultMesaage ( ) , servletResponse) ;
return false ;
} catch ( AuthenticationException e) {
if ( e. getCause ( ) instanceof BusinessException ) {
BusinessException err= ( BusinessException) e. getCause ( ) ;
resultResponse ( err. getMessageCode ( ) , err. getDefaultMesaage ( ) , servletResponse) ;
} else {
resultResponse ( 400001 , "使用者的認證是失敗的" , servletResponse) ;
}
return false ;
} catch ( AuthorizationException e) {
if ( e. getCause ( ) instanceof BusinessException ) {
BusinessException err= ( BusinessException) e. getCause ( ) ;
resultResponse ( err. getMessageCode ( ) , err. getDefaultMesaage ( ) , servletResponse) ;
} else {
resultResponse ( 403001 , "使用者沒有訪問許可權" , servletResponse) ;
}
return false ;
} catch ( Exception e) {
if ( e. getCause ( ) instanceof BusinessException ) {
BusinessException err= ( BusinessException) e. getCause ( ) ;
resultResponse ( err. getMessageCode ( ) , err. getDefaultMesaage ( ) , servletResponse) ;
} else {
resultResponse ( 500001 , "系統出現了異常" , servletResponse) ;
}
return false ;
}
return true ;
}
private void resultResponse ( int messageCode, String defaultMesaage, ServletResponse response) {
JSONObject jsonObject = new JSONObject ( ) ;
jsonObject. put ( "code" , messageCode) ;
jsonObject. put ( "msg" , defaultMesaage) ;
response. setContentType ( MediaType. APPLICATION_JSON_UTF8. toString ( ) ) ;
try {
ServletOutputStream out = response. getOutputStream ( ) ;
out. write ( jsonObject. toJSONString ( ) . getBytes ( ) ) ;
out. flush ( ) ;
} catch ( IOException e) {
e. printStackTrace ( ) ;
}
}
}
4、編寫憑證匹配器
public class CustomHashedCredentialsMatcher extends HashedCredentialsMatcher {
@Autowired
private IUserService userService;
@Override
public boolean doCredentialsMatch ( AuthenticationToken token, AuthenticationInfo info) {
CustomToken token1= ( CustomToken) token;
String tokenVal= ( String) token1. getPrincipal ( ) ;
boolean b= false ;
try {
b = userService. tokenExistsOrNot ( tokenVal) ;
} catch ( Exception err) {
throw new BusinessException ( 500001 , "查詢token存在失敗" + err. getMessage ( ) ) ;
}
if ( ! b) {
throw new BusinessException ( 4010000 , "授權資訊無效請重新登入" ) ;
}
return true ;
}
}
5、編寫realm
public class CustomRealm extends AuthorizingRealm {
@Override
protected AuthorizationInfo doGetAuthorizationInfo ( PrincipalCollection principalCollection) {
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo ( ) ;
return simpleAuthorizationInfo;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo ( AuthenticationToken authenticationToken) throws AuthenticationException {
CustomToken customToken= ( CustomToken) authenticationToken;
String token= ( String) customToken. getPrincipal ( ) ;
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo ( token, token, getName ( ) ) ;
return simpleAuthenticationInfo;
}
}
6、編寫Swagger的配置檔案
@SpringBootConfiguration
@EnableSwagger2
public class SwaggerConfig {
@Bean
public Docket createApi ( ) {
return new Docket ( DocumentationType. SWAGGER_2)
. apiInfo ( apiInfo ( ) )
. select ( )
. apis ( RequestHandlerSelectors. basePackage ( "com.test.shiro.controller" ) )
. paths ( PathSelectors. any ( ) )
. build ( ) ;
}
private ApiInfo apiInfo ( ) {
return new ApiInfoBuilder ( )
. title ( "springboot+shiro+swagger測試" )
. description ( "這裡是整合shiro和Swagger實現前後分離" )
. version ( "v1.0" )
. build ( ) ;
}
}
7、編寫shiro的配置檔案
@SpringBootConfiguration
public class ShiroConfig {
@Bean
public CustomRealm customRealm ( ) {
CustomRealm customRealm = new CustomRealm ( ) ;
customRealm. setCredentialsMatcher ( customHashedCredentialsMatcher ( ) ) ;
return customRealm;
}
@Bean
public CustomHashedCredentialsMatcher customHashedCredentialsMatcher ( ) {
return new CustomHashedCredentialsMatcher ( ) ;
}
@Bean
public DefaultWebSecurityManager securityManager ( ) {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager ( ) ;
securityManager. setRealm ( customRealm ( ) ) ;
return securityManager;
}
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean ( DefaultWebSecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean ( ) ;
shiroFilterFactoryBean. setSecurityManager ( securityManager) ;
Map< String, Filter> maps= new LinkedHashMap < > ( ) ;
maps. put ( "token" , new CustomAccessControllerFilter ( ) ) ;
shiroFilterFactoryBean. setFilters ( maps) ;
Map< String, String> maps1= new LinkedHashMap < > ( ) ;
maps1. put ( "/user/login" , "anon" ) ;
maps1. put ( "/swagger/**" , "anon" ) ;
maps1. put ( "/v2/api-docs" , "anon" ) ;
maps1. put ( "/swagger-ui.html" , "anon" ) ;
maps1. put ( "/swagger-resources/**" , "anon" ) ;
maps1. put ( "/webjars/**" , "anon" ) ;
maps1. put ( "/favicon.ico" , "anon" ) ;
maps1. put ( "/captcha.jpg" , "anon" ) ;
maps1. put ( "/csrf" , "anon" ) ;
maps1. put ( "/**" , "token,authc" ) ;
shiroFilterFactoryBean. setFilterChainDefinitionMap ( maps1) ;
return shiroFilterFactoryBean;
}
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor ( DefaultSecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor attributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor ( ) ;
attributeSourceAdvisor. setSecurityManager ( securityManager) ;
return attributeSourceAdvisor;
}
@Bean
@ConditionalOnMissingBean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator ( ) {
DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator ( ) ;
defaultAdvisorAutoProxyCreator. setProxyTargetClass ( true ) ;
return defaultAdvisorAutoProxyCreator;
}
}
8、編寫專案的配置檔案
@SpringBootConfiguration
@ComponentScan ( basePackages = { "com.qf.shiro" } )
@MapperScan ( basePackages = { "com.qf.shiro.mapper" } )
public class AppConfig {
}
9、結果集的封裝
9.1、介面的封裝
public interface ResponseCodeInterface {
int getCode ( ) ;
String getMsg ( ) ;
}
9.2、返回資訊碼值和提示資訊的封裝
public enum BaseResponseCode implements ResponseCodeInterface {
SUCCESS ( 0 , "操作成功" ) ,
SYSTEM_ERROR ( 5000001 , "系統錯誤" ) ,
METHOD_INVALIDATE ( 4000001 , "資料校驗出錯" ) ,
DATA_ERROR ( 4000002 , "傳入資料異常" ) ,
TOKEN_NOT_NULL ( 4010001 , "使用者認證異常" ) ;
BaseResponseCode ( int code, String msg) {
this . code= code;
this . msg= msg;
}
private int code;
private String msg;
@Override
public int getCode ( ) {
return code;
}
@Override
public String getMsg ( ) {
return msg;
}
}
9.3、返回資料的封裝
@Data
public class DataResult < T> {
private int code;
private String msg;
private T data;
public DataResult ( int code, T data) {
this . code= code;
this . data= data;
this . msg= null;
}
public DataResult ( int code, String msg, T data) {
this . code= code;
this . data= data;
this . msg= msg;
}
public DataResult ( int code, String msg) {
this . code= code;
this . msg= msg;
}
public DataResult ( ) {
this . code= BaseResponseCode. SUCCESS. getCode ( ) ;
this . msg= BaseResponseCode. SUCCESS. getMsg ( ) ;
this . data= null;
}
public DataResult ( T data) {
this . code= BaseResponseCode. SUCCESS. getCode ( ) ;
this . msg= BaseResponseCode. SUCCESS. getMsg ( ) ;
this . data= data;
}
public DataResult ( ResponseCodeInterface responseCodeInterface) {
this . data= null;
this . code= responseCodeInterface. getCode ( ) ;
this . msg= responseCodeInterface. getMsg ( ) ;
}
public DataResult ( ResponseCodeInterface responseCodeInterface, T data) {
this . data= data;
this . code= responseCodeInterface. getCode ( ) ;
this . msg= responseCodeInterface. getMsg ( ) ;
}
public static < T> DataResult success ( ) {
return new DataResult ( ) ;
}
public static < T> DataResult success ( T data) {
return new DataResult ( data) ;
}
public static < T> DataResult getResult ( int code, String msg, T data) {
return new < T> DataResult ( code, msg, data) ;
}
public static < T> DataResult getResult ( int code, String msg) {
return new < T> DataResult ( code, msg) ;
}
public static < T> DataResult getResult ( BaseResponseCode baseResponseCode) {
return new < T> DataResult ( baseResponseCode) ;
}
public static < T> DataResult getResult ( BaseResponseCode baseResponseCode, T data) {
return new < T> DataResult ( baseResponseCode, data) ;
}
}
10、controller的編寫
@RestController
@Api ( tags = { "使用者介面" } )
public class UserController {
@Autowired
private IUserService userService;
private Logger logger= LoggerFactory. getLogger ( UserController. class ) ;
@RequestMapping ( value = "/user/login" , method = RequestMethod. POST)
@ApiOperation ( value = "使用者登陸的介面" )
public DataResult< User> login ( @RequestBody User user) {
DataResult< User> dataResult= null;
try {
User user1 = userService. login ( user) ;
dataResult= DataResult. success ( user1) ;
} catch ( Exception e) {
if ( e instanceof BusinessException ) {
BusinessException err= ( BusinessException) e;
dataResult= new DataResult < > ( err. getMessageCode ( ) , err. getDefaultMesaage ( ) ) ;
} else {
dataResult= DataResult. getResult ( BaseResponseCode. SYSTEM_ERROR. getCode ( ) , BaseResponseCode. SYSTEM_ERROR. getMsg ( ) ) ;
}
return dataResult;
}
return dataResult;
}
@RequestMapping ( value = "/user/list" , method = RequestMethod. GET)
@ApiOperation ( value = "獲取所有的使用者資訊" )
@ApiImplicitParam ( paramType = "header" , name = "token" , value = "使用者token" , required = true , dataType = "String" )
public DataResult< List< User> > findUserList ( ) {
DataResult< List< User> > userLists;
try {
List< User> users = userService. findUserList ( ) ;
userLists= DataResult. success ( users) ;
logger. info ( "獲取資料成功...." ) ;
} catch ( Exception err) {
logger. error ( "獲取使用者資訊失敗:" + err. fillInStackTrace ( ) ) ;
userLists= DataResult. getResult ( BaseResponseCode. SYSTEM_ERROR. getCode ( ) , BaseResponseCode. SYSTEM_ERROR. getMsg ( ) ) ;
}
return userLists;
}
}
11、Service的編寫
11.1、Service介面的編寫
public interface IUserService {
User login ( User user) ;
User findUserByName ( String userName) ;
void updateToken ( User user) ;
List< User> findUserList ( ) throws Exception;
boolean tokenExistsOrNot ( String token) ;
}
11.2、Service實現的編寫
@Service
@Transactional
public class UserService implements IUserService {
@Autowired
private UserMapper userMapper;
@Override
public User login ( User user) {
String userName = user. getUserName ( ) ;
User userResult = findUserByName ( userName) ;
if ( null== userResult) {
throw new BusinessException ( 40001 , "使用者名稱不對" ) ;
}
if ( ! ( userResult. getPassword ( ) . equals ( user. getPassword ( ) ) ) ) {
throw new BusinessException ( 40002 , "密碼不對" ) ;
}
String token= UUID. randomUUID ( ) . toString ( ) ;
Date date= new Date ( ) ;
userResult. setToken ( token) ;
userResult. setExpireDate ( date) ;
updateToken ( userResult) ;
userResult. setPassword ( "" ) ;
return userResult;
}
@Override
public User findUserByName ( String userName) {
return userMapper. findUserByName ( userName) ;
}
@Override
public void updateToken ( User user) {
userMapper. updateToken ( user) ;
}
@Override
public List< User> findUserList ( ) throws Exception{
List< User> userList = userMapper. findUserList ( ) ;
DataResult< List< User> > dataResult = new DataResult < > ( 0 , "請求完美" , userList) ;
return userList;
}
@Override
public boolean tokenExistsOrNot ( String token) {
User userResult = userMapper. findUserByToken ( token) ;
if ( null!= userResult) {
return true ;
}
return false ;
}
}
13、Mapper介面的編寫
public interface UserMapper {
User findUserByName ( String userName) ;
void updateToken ( User user) ;
List< User> findUserList ( ) ;
User findUserByToken ( String token) ;
}
14、mapper.xml檔案的編寫
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
< mapper namespace = " com.qf.shiro.mapper.UserMapper" >
< select id = " findUserByName" parameterType = " string" resultType = " user" >
select * from t_user where userName=#{value}
</ select>
< update id = " updateToken" parameterType = " user" >
update t_user set token=#{token} where id=#{id}
</ update>
< select id = " findUserList" resultType = " user" >
select * from t_user
</ select>
< select id = " findUserByToken" parameterType = " String" resultType = " user" >
select * from t_user where token=#{value}
</ select>
</ mapper>
15、使用者物件的編寫
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User implements Serializable {
private static final long serialVersionUID = - 5199670739008077879 L;
private Integer id;
private String userName;
private String password;
private String token;
private Date expireDate;
}
16、常量類的編寫
public class Constant {
public static final String REQ_TOKEN= "token" ;
}
17、properties檔案的編寫
mybatis.type-aliases-package=com.test.shiro.pojo
mybatis.mapper-locations=classpath:mapper/*.xml
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql:///xxx
spring.datasource.username=root
spring.datasource.password=root
18、自定義異常的編寫
public class BusinessException extends RuntimeException {
private int messageCode;
private String defaultMesaage;
public BusinessException ( int messageCode, String defaultMesaage) {
super ( defaultMesaage) ;
this . messageCode= messageCode;
this . defaultMesaage= defaultMesaage;
}
public String getDefaultMesaage ( ) {
return defaultMesaage;
}
public int getMessageCode ( ) {
return messageCode;
}
}