spring mvc集成spring security案例

文章首發(fā) http://surpass.esy.es

代碼結(jié)構(gòu)

總共涉及到7個類文件和添加角色配置文件以及需要在web.xml中添加一個過濾器(如果是spring boot的話則不需要配置web.xml)

代碼結(jié)構(gòu)

涉及文件及其作用簡單分析

  1. role-config.properties:用來配置所有角色及所持權(quán)限信息
  2. Role.java:將配置文件中的權(quán)限信息分裝到j(luò)ava類中,方便處理
  3. RoleHandler.java:構(gòu)建Role對象列表齿诞,以便可以通過配置文件的key獲取到相關(guān)Role對象
  4. 'UserDtailsBean.java':根絕具體業(yè)務需求來給用戶對象添加(自定義)security需要的屬性
  5. 'UserDetailsServiceCustom':自定義用戶信息獲取服務白对,主要是重寫loadUserByUsername方法
  6. AuthenticationProviderCustom.java:自定義授權(quán)認證類界酒,當用戶登錄,則會調(diào)用該方法,授權(quán)操作就是在這里進行的
  7. CustomSuccessHandler:當?shù)卿洺晒蟮奶幚聿僮鳎梢允∪セ蛑苯犹D(zhuǎn)到某個頁面
  8. SecurityConfig:Security的核心配置類楼吃,在這里使用了自定義的AuthenticationProviderCustom和AuthenticationProviderCustom進行用戶信息的構(gòu)建以及授權(quán)類,具體配置內(nèi)容可參考官網(wǎng)(當然,也可以用XML進行配置妄讯,而不是Java Config的形式)
  9. web.xml:配置security過濾器(使用spring boot的話無須配置)

具體代碼案例

1.在配置文件中配置好各個角色的信息孩锡,security會默認解析以ROLE_開頭的權(quán)限內(nèi)容作為角色,例如ROLE_ADMIN亥贸,則將作為ADMIN角色

角色配置

2.添加角色Bean對象

import java.util.List;

public class Role {
    private String name;    //  名稱
    private List<String> privileges;    //  分配的角色/權(quán)限

    Role(){

    }

    Role(String name, List<String> privileges){
        this.name = name;
        this.privileges = privileges;
    }

    List<String> getPrivileges(){
        return this.privileges;
    }

    String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

3.提供角色權(quán)限初始化類

import com.x.x.common.UserType;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;

/**
 * 角色權(quán)限管理以及初始化
 * 目前是讀取配置文件來初始化用戶權(quán)限
 * <p>
 * Created by surpass.wei@gmail.com on 2017/3/6.
 */
@Component
public class RoleHandler {

    @Value("${role.admin}")
    private String adminRoleStr;

    @Value("${role.teacher}")
    private String teacherRoleStr;

    @Value("${role.trainee}")
    private String traineeRoleStr;

    private final List<Role> roleList = new ArrayList<>();

    /**
     * 根據(jù)config.properties配置文件中的角色權(quán)限控制來初始化roles
     */
    private void init() {
        UserType[] userTypes = UserType.values();

        Role role;
        List<String> privilegeList;
        /**
         * 循環(huán)角色類型獲取相應的配置字符串躬窜,根據(jù)“,”分隔
         * 構(gòu)建Role的List
         */
        for (UserType userType : userTypes) {

            String currentRoleStr;  //  當前角色的權(quán)限信息
            if (userType.equals(UserType.ADMIN)) {
                currentRoleStr = adminRoleStr;
            } else if (userType.equals(UserType.TEACHER)) {
                currentRoleStr = teacherRoleStr;
            } else if (userType.equals(UserType.TRAINEE)) {
                currentRoleStr = traineeRoleStr;
            } else {
                continue;
            }
            if (currentRoleStr.isEmpty()) {
                return;
            }

            privilegeList = new ArrayList<>();
            String[] roleArr = currentRoleStr.split(",");
            for (String s : roleArr) {
                privilegeList.add(s.trim());
            }
            role = new Role(userType.getRoleName(), privilegeList);

            roleList.add(role);
        }

    }

    public Role getRole(String roleName) {
        if (roleList.isEmpty()) {
            this.init();
        }

        if (roleName == null || roleName.isEmpty()) {
            return null;
        }

        Role role = new Role();
        for (Role temp_role : roleList) {
            if (temp_role.getName().compareToIgnoreCase(roleName) == 0) {
                role = temp_role;
            }
        }

        return role;
    }
}

4.封裝UserDetailBean

import com.x.x.common.UserType;
import com.x.x.entity.User;
import com.x.x.util.BeanUtil;
import com.x.x.util.SpringContextUtil;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import java.util.ArrayList;
import java.util.Collection;

/**
 * 基于User包裝一個UserDetailsBean(用于security權(quán)限認證)
 * 主要為了實現(xiàn) getAuthorities()
 */
public class UserDetailsBean extends User implements UserDetails {

    public UserDetailsBean(User user) {
        BeanUtil.copyObjValue(user, this);
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        //  獲取用戶的角色/權(quán)限信息

        Collection<SimpleGrantedAuthority> authorities = new ArrayList<>();
        RoleHandler roleHandler = SpringContextUtil.getApplicationContext().getBean(RoleHandler.class);

        if (roleHandler == null) {
            return null;
        }

        // 根據(jù)用戶的類型, 構(gòu)建用戶對應的Role
        String roleName;

        roleName = UserType.getRoleName(this.getUserType());
        Role role = roleHandler.getRole(roleName);

        //  循環(huán)角色的權(quán)限信息, 構(gòu)建authorities
        for (String privilege : role.getPrivileges()) {
            authorities.add(new SimpleGrantedAuthority(privilege));
        }

        return authorities;
    }

    @Override
    public String getPassword() {
        return this.getPwd();
    }

    @Override
    public String getUsername() {
        return this.getLoginName();
    }

    @Override
    public boolean isAccountNonExpired() {
        //  賬戶是否未過期
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        //  賬戶是否未鎖定
        return this.getUserState() == 0;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        // 憑證是否未過期
        return true;
    }

    @Override
    public boolean isEnabled() {
        //  是否有效
        return true;
    }
}

5.實現(xiàn)UserDetailsService重寫loadUserByUsername方法

import com.x.x.entity.User;
import com.x.x.service.IUserService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;

/**
 * 重寫loadUserByUsername
 * Created by surpass.wei@gmail.com on 2016/12/16.
 */
@Component
public class UserDetailsServiceCustom implements UserDetailsService {
    private Logger logger = LoggerFactory.getLogger(UserDetailsServiceCustom.class);

    @Resource
    private IUserService userService;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {

        UserDetailsBean userDetailsBean;

        User search = new User();
        search.setLoginName(username);
        User user = userService.findOneByEntity(search);

        if (user==null) {
            logger.warn("該用戶不存在:" + username);
            throw new UsernameNotFoundException("該用戶不存在:" + username);
        } else {
            userDetailsBean = new UserDetailsBean(user);
        }

        return userDetailsBean;
    }
}

6.自定義的授權(quán)認證類

import com.x.x.entity.User;
import com.x.x.service.IUserService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;

@Component
public class AuthenticationProviderCustom implements AuthenticationProvider {
    private Logger logger = LoggerFactory.getLogger(AuthenticationProviderCustom.class);

    @Resource
    private IUserService userService;

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        UsernamePasswordAuthenticationToken token = (UsernamePasswordAuthenticationToken) authentication;
        String loginName = token.getName();
        String password = token.getCredentials().toString();
        User user = userService.getUserByLoginNameAndPwd(loginName, password);

        UserDetailsBean userDetailsBean;
        if(user != null) {
            userDetailsBean = new UserDetailsBean(user);
        } else {
            logger.error("用戶名或密碼錯誤");
            throw new BadCredentialsException("用戶名或密碼錯誤");
        }

        return new UsernamePasswordAuthenticationToken(userDetailsBean, password, userDetailsBean.getAuthorities());
    }

    @Override
    public boolean supports(Class<?> aClass) {
        return true;
    }
}

7.配置Security

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;

import javax.annotation.Resource;


@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
@EnableWebSecurity //這里如果放開js文件將不被允許執(zhí)行,注釋掉也沒有影響炕置,不知道為什么
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Resource
    private CustomSuccessHandler customSuccessHandler;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/**").permitAll()
                //.antMatchers("/static/**").permitAll()  //  允許所有用戶對根路徑以及匹配"/static/"開頭的路徑的訪問
                //.antMatchers("/", "/index").hasRole("TEACHER")
                .anyRequest().authenticated()   //  任何尚未匹配的的URL地址只需要對用戶進行權(quán)限驗證
                .and()
                .formLogin()
                    .successHandler(customSuccessHandler)
                    .failureUrl("/login?error=true")
                    //.defaultSuccessUrl("/home")
                    //.defaultSuccessUrl("/swagger-ui.html")  //  登陸成功后默認默認跳轉(zhuǎn)到swagger頁
                    .permitAll()
                    .and()
                    .rememberMe().tokenValiditySeconds(604800);
        http.logout();
        http.csrf().disable();
    }

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        //  將驗證過程交給自定義的授權(quán)認證類
        auth.authenticationProvider(authenticationProvider());
    }

    @Bean
    @Override
    protected UserDetailsService userDetailsService() {
        return new UserDetailsServiceCustom();
    }

    @Bean
    public AuthenticationProvider authenticationProvider() {
        return new AuthenticationProviderCustom();
    }
}

8.自定義成功處理類(可選)

import org.springframework.security.core.Authentication;
import org.springframework.security.web.DefaultRedirectStrategy;
import org.springframework.security.web.RedirectStrategy;
import org.springframework.security.web.authentication.SimpleUrlAuthenticationSuccessHandler;
import org.springframework.stereotype.Component;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;


@Component
public class CustomSuccessHandler extends SimpleUrlAuthenticationSuccessHandler {
    private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();

    @Override
    protected void handle(HttpServletRequest request, HttpServletResponse response,
                          Authentication authentication) throws IOException, ServletException {
        UserDetailsBean user = (UserDetailsBean) authentication.getPrincipal();

        String targetUrl;

        if (UserType.ADMIN.getValue().equals(user.getUserType())) {
            targetUrl = "/swagger-ui.html";
        } else if (UserType.TEACHER.getValue().equals(user.getUserType())) {
            targetUrl = "/teacher";
        } else if (UserType.TRAINEE.getValue().equals(user.getUserType())){
            targetUrl = "/trainee";
        } else {
            targetUrl = "/login?error";
        }

        redirectStrategy.sendRedirect(request, response, targetUrl);
    }
}

9.添加security過濾器

  <!--spring security-->
  <filter>
    <filter-name>springSecurityFilterChain</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
  </filter>

  <filter-mapping>
    <filter-name>springSecurityFilterChain</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末荣挨,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子朴摊,更是在濱河造成了極大的恐慌默垄,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,376評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件甚纲,死亡現(xiàn)場離奇詭異口锭,居然都是意外死亡,警方通過查閱死者的電腦和手機介杆,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,126評論 2 385
  • 文/潘曉璐 我一進店門鹃操,熙熙樓的掌柜王于貴愁眉苦臉地迎上來况既,“玉大人,你說我怎么就攤上這事组民。” “怎么了悲靴?”我有些...
    開封第一講書人閱讀 156,966評論 0 347
  • 文/不壞的土叔 我叫張陵臭胜,是天一觀的道長。 經(jīng)常有香客問我癞尚,道長耸三,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,432評論 1 283
  • 正文 為了忘掉前任浇揩,我火速辦了婚禮仪壮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘胳徽。我一直安慰自己积锅,他們只是感情好,可當我...
    茶點故事閱讀 65,519評論 6 385
  • 文/花漫 我一把揭開白布养盗。 她就那樣靜靜地躺著缚陷,像睡著了一般。 火紅的嫁衣襯著肌膚如雪往核。 梳的紋絲不亂的頭發(fā)上箫爷,一...
    開封第一講書人閱讀 49,792評論 1 290
  • 那天,我揣著相機與錄音聂儒,去河邊找鬼虎锚。 笑死,一個胖子當著我的面吹牛衩婚,可吹牛的內(nèi)容都是我干的窜护。 我是一名探鬼主播,決...
    沈念sama閱讀 38,933評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼谅猾,長吁一口氣:“原來是場噩夢啊……” “哼柄慰!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起税娜,我...
    開封第一講書人閱讀 37,701評論 0 266
  • 序言:老撾萬榮一對情侶失蹤坐搔,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后敬矩,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體概行,經(jīng)...
    沈念sama閱讀 44,143評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,488評論 2 327
  • 正文 我和宋清朗相戀三年弧岳,在試婚紗的時候發(fā)現(xiàn)自己被綠了凳忙。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片业踏。...
    茶點故事閱讀 38,626評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖涧卵,靈堂內(nèi)的尸體忽然破棺而出勤家,到底是詐尸還是另有隱情,我是刑警寧澤柳恐,帶...
    沈念sama閱讀 34,292評論 4 329
  • 正文 年R本政府宣布伐脖,位于F島的核電站,受9級特大地震影響乐设,放射性物質(zhì)發(fā)生泄漏讼庇。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,896評論 3 313
  • 文/蒙蒙 一近尚、第九天 我趴在偏房一處隱蔽的房頂上張望蠕啄。 院中可真熱鬧,春花似錦戈锻、人聲如沸歼跟。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,742評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽嘹承。三九已至,卻和暖如春如庭,著一層夾襖步出監(jiān)牢的瞬間叹卷,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工坪它, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留骤竹,地道東北人。 一個月前我還...
    沈念sama閱讀 46,324評論 2 360
  • 正文 我出身青樓往毡,卻偏偏與公主長得像蒙揣,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子开瞭,可洞房花燭夜當晚...
    茶點故事閱讀 43,494評論 2 348

推薦閱讀更多精彩內(nèi)容