springboot整合shiro實現身份認證

weixin_34104341發表於2020-04-07

 github地址:https://github.com/peterowang/shiro

 

pom檔案

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!-- mysql驅動; -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>net.sourceforge.nekohtml</groupId>
<artifactId>nekohtml</artifactId>
<version>1.9.22</version>
</dependency>

<!--shiro-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.2.2</version>
</dependency>

<!-- Spirng data JPA依賴; -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
</dependency>
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.2</version>
<exclusions>
<exclusion>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>

1.整合spring data jpa 

2.使用thymeleaf   見application.properties

spring.application.name=spring-boot-shiro
server.port=8080

spring.thymeleaf.mode=LEGACYHTML5 這裡的設定主要是因為thymeleaf校驗html檔案的時候會特別嚴格,比如<input> 必須加上/結尾,這裡需要依賴nekohtml.
spring.thymeleaf.cache=false
spring.thymeleaf.prefix=classpath:/templates/ 這裡相當於springmvc裡的檢視解析器
spring.thymeleaf.suffix=.html


########################################################
###datasource
########################################################
spring.datasource.url = jdbc:mysql://localhost:3306/mydb?useUnicode=true&characterEncoding=utf8
spring.datasource.username = root
spring.datasource.password = root
spring.datasource.driverClassName = com.mysql.jdbc.Driver
spring.datasource.max-active=20
spring.datasource.min-active=10
spring.datasource.max-idle=8
spring.datasource.initial-size=10



########################################################
### Java Persistence Api
########################################################
# Specify the DBMS
spring.jpa.database = MYSQL
# Show or not log for each sql query
spring.jpa.show-sql = true
# Hibernate ddl auto (create, create-drop, update)
spring.jpa.hibernate.ddl-auto = update
# Naming strategy
#[org.hibernate.cfg.ImprovedNamingStrategy | org.hibernate.cfg.DefaultNamingStrategy]
spring.jpa.hibernate.naming-strategy = org.hibernate.cfg.DefaultNamingStrategy
# stripped before adding them to the entity manager)
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5Dialect
目錄結構 :

3.配置realm

Realm是一個Dao,通過它來驗證使用者身份和許可權。這裡Shiro不做許可權的管理工作,需要我們自己管理使用者許可權,只需要從我們的資料來源中把使用者和使用者的角色許可權資訊取出來交給Shiro即可。
config包下再建一個包Shiro,然後在Shiro包下建一個MyShiroRealm類,繼承AuthorizingRealm抽象類。
package com.example.demo.config.shiro;

import com.example.demo.model.UserInfo;
import com.example.demo.service.UserInfoService;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

/**
* Created by BFD-593 on 2017/8/8.
*/
public class MyShiroRealm extends AuthorizingRealm{
private static final Logger logger = LoggerFactory.getLogger(MyShiroRealm.class);
@Autowired
private UserInfoService userInfoService;

@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
logger.info("開始身份驗證");
String username = (String) token.getPrincipal();
UserInfo userInfo = userInfoService.findByUsername(username);
if(userInfo==null) {
return null;
}
SimpleAuthenticationInfo auth = new SimpleAuthenticationInfo(
userInfo,
userInfo.getPassword(), //密碼
getName() //realm name
);
return auth;
}

@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
return null;
}
}

4.配置自定義密碼比較器(shiro會根據這個來將使用者輸入的密碼加密成註冊時的密碼,與db裡的密碼比較)
package com.example.demo.config.shiro;

import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authc.credential.SimpleCredentialsMatcher;
import org.apache.shiro.crypto.hash.Md5Hash;

/**
* Created by BFD-593 on 2017/8/8.
*/
public class CredentialsMatcher extends SimpleCredentialsMatcher {

@Override
public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
UsernamePasswordToken utoken=(UsernamePasswordToken) token;
//獲得使用者輸入的密碼:(可以採用加鹽(salt)的方式去檢驗)
String password = String.valueOf(utoken.getPassword());
String username = String.valueOf(utoken.getUsername());
Md5Hash md5 = new Md5Hash(password,username, 2);
String inPassword = md5.toString();
//獲得資料庫中的密碼
String dbPassword=(String) info.getCredentials();
//進行密碼的比對
return this.equals(inPassword, dbPassword);
}

}
5.shiroConfig配置類
這裡要配置的是ShiroConfig類,Apache Shiro 核心通過 Filter 來實現,就好像SpringMvc 通過DispachServlet 來主控制一樣。 既然是使用 Filter 一般也就能猜到,是通過URL規則來進行過濾和許可權校驗,所以我們需要定義一系列關於URL的規則和訪問許可權。
package com.example.demo.config.shiro;

import org.apache.shiro.mgt.SecurityManager;
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;
import java.util.Map;

/**
* Created by BFD-593 on 2017/8/8.
*/
@Configuration
public class ShiroConfiguration {
@Bean(name="shiroFilter")
public ShiroFilterFactoryBean shiroFilterFactoryBean(@Qualifier("securityManager") SecurityManager manager) {

ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(manager);

Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
filterChainDefinitionMap.put("/logout", "logout");
filterChainDefinitionMap.put("/favicon.ico", "anon");
filterChainDefinitionMap.put("/**", "authc");
//authc表示需要驗證身份才能訪問,還有一些比如anon表示不需要驗證身份就能訪問等。
//關於為什麼設定filterChainDefinitionMap.put("/favicon.ico", "anon");,請參考Shiro登入後下載favicon.ico問題

shiroFilterFactoryBean.setLoginUrl("/login");
shiroFilterFactoryBean.setSuccessUrl("/index");
// shiroFilterFactoryBean.setUnauthorizedUrl("/403"); //這裡設定403並不會起作用,參考http://www.jianshu.com/p/e03f5b54838c

shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}


/**
* 不指定名字的話,自動建立一個方法名第一個字母小寫的bean
* @Bean(name = "securityManager")
* @return
*/
@Bean(name="securityManager")
public SecurityManager securityManager(@Qualifier("authRealm") MyShiroRealm authRealm) {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(authRealm);
return securityManager;
}

/**
* Shiro Realm 繼承自AuthorizingRealm的自定義Realm,即指定Shiro驗證使用者登入的類為自定義的
*
* @param
* @return
*/
@Bean(name="authRealm")
public MyShiroRealm userRealm(@Qualifier("credentialsMatcher") CredentialsMatcher matcher) {
MyShiroRealm userRealm = new MyShiroRealm();
//告訴realm,使用credentialsMatcher加密演算法類來驗證密文
userRealm.setCredentialsMatcher(matcher);
return userRealm;
}

/**
* 密碼比較器
* @return
*/
@Bean(name="credentialsMatcher")
public CredentialsMatcher credentialsMatcher() {
return new CredentialsMatcher();
}
}

6.web層
package com.example.demo.web;

import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.servlet.http.HttpServletRequest;
import java.util.Map;

/**
* Created by BFD-593 on 2017/8/8.
*/
@Controller
public class HomeController {
@RequestMapping({"/","/index"})
public String index() {
return "index";
}

/**
* logout由shiro實現,我們只需提供入口即可
* filterChainDefinitionMap.put("/logout", "logout");
* @return
*/
@RequestMapping({"/logout"})
public String logout(){
return "login";
}

/**
* 因為設定了setLoginUrl("/login");登入url如果沒有登入,
* 所有的請求都傳送到這裡。shiro會自動呼叫securityManager
* 此方法不處理登入成功,由shiro進行處理。
* @param request
* @param map
* @return
* @throws Exception
*/
@RequestMapping("/login")
public String login(HttpServletRequest request, Map<String, Object> map) throws Exception {
// 登入失敗從request中獲取shiro處理的異常資訊。
// shiroLoginFailure:就是shiro異常類的全類名.
String exceptionClassName = (String)request.getAttribute("shiroLoginFailure");
String error = null;
if(UnknownAccountException.class.getName().equals(exceptionClassName)) {
error = "使用者名稱/密碼錯誤";
} else if(IncorrectCredentialsException.class.getName().equals(exceptionClassName)) {
error = "使用者名稱/密碼錯誤";
} else if(exceptionClassName != null) {
error = "其他錯誤:" + exceptionClassName;
}
map.put("msg", error);
return "login";
}
}
7.UserInfo:
package com.example.demo.model;

import javax.persistence.*;
import java.io.Serializable;

/**
* Created by archerlj on 2017/6/30.
*/

@Entity
@Table(name="user_info")
public class UserInfo implements Serializable {

private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;//使用者id;

@Column(unique = true, name = "username")
private String username;//賬號.
@Column(name = "password")
private String password; //密碼;
@Column(name = "salt")
private String salt;//加密密碼的鹽
@Column(name = "state")
private byte state;//使用者狀態,0:建立未認證(比如沒有啟用,沒有輸入驗證碼等等)--等待驗證的使用者 , 1:正常狀態,2:使用者被鎖定.


public long getId() {
return id;
}

public void setId(long id) {
this.id = id;
}

public String getUsername() {
return username;
}

public void setUsername(String username) {
this.username = username;
}

public String getPassword() {
return password;
}

public void setPassword(String password) {
this.password = password;
}

public String getSalt() {
return salt;
}

public void setSalt(String salt) {
this.salt = salt;
}

public byte getState() {
return state;
}

public void setState(byte state) {
this.state = state;
}


}
8.UserInfoRespository
package com.example.demo.dao;

import com.example.demo.model.UserInfo;
import org.springframework.data.jpa.repository.JpaRepository;

/**
* Created by BFD-593 on 2017/8/8.
*/
public interface UserInfoRepository extends JpaRepository<UserInfo,Long> {
public UserInfo findByUsername(String username);

public UserInfo save(UserInfo userInfo);
}
9.UserInfoService:
import com.example.demo.dao.UserInfoRepository;
import com.example.demo.model.UserInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
* Created by BFD-593 on 2017/8/8.
*/
@Service
public class UserInfoService {
@Autowired
private UserInfoRepository userInfoRepository;

public UserInfo findByUsername(String username){
return userInfoRepository.findByUsername(username);
}
}
10.配置jpa請看之前部落格

轉載於:https://www.cnblogs.com/wangjing666/p/7323782.html

相關文章